Modify build feature names as Tizen 3.0 build option naming rule
[platform/upstream/bluez.git] / tools / btsnoop.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2011-2012  Intel Corporation
6  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #include <stdbool.h>
36 #include <string.h>
37 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
38 #include <time.h>
39 #include <sys/time.h>
40 #endif
41 #include <getopt.h>
42 #include <endian.h>
43 #include <arpa/inet.h>
44 #include <sys/stat.h>
45
46 #include "src/shared/btsnoop.h"
47
48 struct btsnoop_hdr {
49         uint8_t         id[8];          /* Identification Pattern */
50         uint32_t        version;        /* Version Number = 1 */
51         uint32_t        type;           /* Datalink Type */
52 } __attribute__ ((packed));
53 #define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
54
55 struct btsnoop_pkt {
56         uint32_t        size;           /* Original Length */
57         uint32_t        len;            /* Included Length */
58         uint32_t        flags;          /* Packet Flags */
59         uint32_t        drops;          /* Cumulative Drops */
60         uint64_t        ts;             /* Timestamp microseconds */
61         uint8_t         data[0];        /* Packet Data */
62 } __attribute__ ((packed));
63 #define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
64
65 static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
66                                       0x6f, 0x6f, 0x70, 0x00 };
67
68 static const uint32_t btsnoop_version = 1;
69
70 static int create_btsnoop(const char *path)
71 {
72         struct btsnoop_hdr hdr;
73         ssize_t written;
74         int fd;
75
76         fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
77                                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
78         if (fd < 0) {
79                 perror("failed to output file");
80                 return -1;
81         }
82
83         memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
84         hdr.version = htobe32(btsnoop_version);
85         hdr.type = htobe32(2001);
86
87         written = write(fd, &hdr, BTSNOOP_HDR_SIZE);
88         if (written < 0) {
89                 perror("failed to write output header");
90                 close(fd);
91                 return -1;
92         }
93
94         return fd;
95 }
96
97 static int open_btsnoop(const char *path, uint32_t *type)
98 {
99         struct btsnoop_hdr hdr;
100         ssize_t len;
101         int fd;
102
103         fd = open(path, O_RDONLY | O_CLOEXEC);
104         if (fd < 0) {
105                 perror("failed to open input file");
106                 return -1;
107         }
108
109         len = read(fd, &hdr, BTSNOOP_HDR_SIZE);
110         if (len < 0 || len != BTSNOOP_HDR_SIZE) {
111                 perror("failed to read input header");
112                 close(fd);
113                 return -1;
114         }
115
116         if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) {
117                 fprintf(stderr, "not a valid btsnoop header\n");
118                 close(fd);
119                 return -1;
120         }
121
122         if (be32toh(hdr.version) != btsnoop_version) {
123                 fprintf(stderr, "invalid btsnoop version\n");
124                 close(fd);
125                 return -1;
126         }
127
128         if (type)
129                 *type = be32toh(hdr.type);
130
131         return fd;
132 }
133
134 #define MAX_MERGE 8
135
136 static void command_merge(const char *output, int argc, char *argv[])
137 {
138         struct btsnoop_pkt input_pkt[MAX_MERGE];
139         unsigned char buf[2048];
140         int output_fd, input_fd[MAX_MERGE], num_input = 0;
141         int i, select_input;
142         ssize_t len, written;
143         uint32_t toread, flags;
144         uint16_t index, opcode;
145
146         if (argc > MAX_MERGE) {
147                 fprintf(stderr, "only up to %d files allowed\n", MAX_MERGE);
148                 return;
149         }
150
151         for (i = 0; i < argc; i++) {
152                 uint32_t type;
153                 int fd;
154
155                 fd = open_btsnoop(argv[i], &type);
156                 if (fd < 0)
157                         break;
158
159                 if (type != 1002) {
160                         fprintf(stderr, "unsupported link data type %u\n",
161                                                                         type);
162                         close(fd);
163                         break;
164                 }
165
166                 input_fd[num_input++] = fd;
167         }
168
169         if (num_input != argc) {
170                 fprintf(stderr, "failed to open all input files\n");
171                 goto close_input;
172         }
173
174         output_fd = create_btsnoop(output);
175         if (output_fd < 0)
176                 goto close_input;
177
178         for (i = 0; i < num_input; i++) {
179                 len = read(input_fd[i], &input_pkt[i], BTSNOOP_PKT_SIZE);
180                 if (len < 0 || len != BTSNOOP_PKT_SIZE) {
181                         close(input_fd[i]);
182                         input_fd[i] = -1;
183                 }
184         }
185
186 next_packet:
187         select_input = -1;
188
189         for (i = 0; i < num_input; i++) {
190                 uint64_t ts;
191
192                 if (input_fd[i] < 0)
193                         continue;
194
195                 if (select_input < 0) {
196                         select_input = i;
197                         continue;
198                 }
199
200                 ts = be64toh(input_pkt[i].ts);
201
202                 if (ts < be64toh(input_pkt[select_input].ts))
203                         select_input = i;
204         }
205
206         if (select_input < 0)
207                 goto close_output;
208
209         toread = be32toh(input_pkt[select_input].size);
210         flags = be32toh(input_pkt[select_input].flags);
211
212         len = read(input_fd[select_input], buf, toread);
213         if (len < 0 || len != (ssize_t) toread) {
214                 close(input_fd[select_input]);
215                 input_fd[select_input] = -1;
216                 goto next_packet;
217         }
218
219         written = htobe32(toread - 1);
220         input_pkt[select_input].size = written;
221         input_pkt[select_input].len = written;
222
223         switch (buf[0]) {
224         case 0x01:
225                 opcode = BTSNOOP_OPCODE_COMMAND_PKT;
226                 break;
227         case 0x02:
228                 if (flags & 0x01)
229                         opcode = BTSNOOP_OPCODE_ACL_RX_PKT;
230                 else
231                         opcode = BTSNOOP_OPCODE_ACL_TX_PKT;
232                 break;
233         case 0x03:
234                 if (flags & 0x01)
235                         opcode = BTSNOOP_OPCODE_SCO_RX_PKT;
236                 else
237                         opcode = BTSNOOP_OPCODE_SCO_TX_PKT;
238                 break;
239         case 0x04:
240                 opcode = BTSNOOP_OPCODE_EVENT_PKT;
241                 break;
242         default:
243                 goto skip_write;
244         }
245
246         index = select_input;
247         input_pkt[select_input].flags = htobe32((index << 16) | opcode);
248
249         written = write(output_fd, &input_pkt[select_input], BTSNOOP_PKT_SIZE);
250         if (written != BTSNOOP_PKT_SIZE) {
251                 fprintf(stderr, "write of packet header failed\n");
252                 goto close_output;
253         }
254
255         written = write(output_fd, buf + 1, toread - 1);
256         if (written != (ssize_t) toread - 1) {
257                 fprintf(stderr, "write of packet data failed\n");
258                 goto close_output;
259         }
260
261 skip_write:
262         len = read(input_fd[select_input],
263                                 &input_pkt[select_input], BTSNOOP_PKT_SIZE);
264         if (len < 0 || len != BTSNOOP_PKT_SIZE) {
265                 close(input_fd[select_input]);
266                 input_fd[select_input] = -1;
267         }
268
269         goto next_packet;
270
271 close_output:
272         close(output_fd);
273
274 close_input:
275         for (i = 0; i < num_input; i++)
276                 close(input_fd[i]);
277 }
278 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
279 #define BT_SNOOP_TYPE_HCI_PREFIX "btsnoop_type_hci"
280
281 static void command_split(const char *input)
282 {
283         unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
284         uint16_t pktlen,opcode;
285         uint32_t type;
286         struct timeval tv;
287         uint16_t index, max_index = 0;
288         char write_file_name[255];
289         struct btsnoop *btsnoop_read_file = NULL;
290         struct btsnoop *btsnoop_write_file[16];
291         time_t t;
292         struct tm tm;
293         unsigned long num_packets = 0;
294
295         btsnoop_read_file = btsnoop_open(input, BTSNOOP_FLAG_PKLG_SUPPORT);
296         if (!btsnoop_read_file)
297                 return;
298
299         type = btsnoop_get_format(btsnoop_read_file);
300         if (type != BTSNOOP_FORMAT_MONITOR) {
301                 fprintf(stderr, "unsupported link data type %u\n", type);
302                 btsnoop_unref(btsnoop_read_file);
303                 return;
304         }
305
306 next_packet:
307         if (!btsnoop_read_hci(btsnoop_read_file, &tv, &index, &opcode, buf,
308                                                                 &pktlen))
309                 goto close_files;
310
311         if (opcode == 0xffff)
312                 goto next_packet;
313
314         switch (opcode) {
315         case BTSNOOP_OPCODE_NEW_INDEX:
316                 t = tv.tv_sec;
317                 localtime_r(&t, &tm);
318
319                 if (max_index < index)
320                         max_index = index;
321
322                 sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log",
323                         BT_SNOOP_TYPE_HCI_PREFIX, index, tm.tm_hour, tm.tm_min,
324                         tm.tm_sec, tv.tv_usec);
325
326                 printf("New Index %d would be saved in %s\n", index,
327                                                         write_file_name);
328
329                 btsnoop_write_file[index] = btsnoop_create(write_file_name,
330                                 BTSNOOP_FORMAT_HCI, -1, -1);
331                 if (!btsnoop_write_file[index])
332                         goto close_files;
333
334                 break;
335         case BTSNOOP_OPCODE_DEL_INDEX:
336                 printf("Del Index %d\n", index);
337
338                 btsnoop_unref(btsnoop_write_file[index]);
339                 btsnoop_write_file[index] = NULL;
340                 break;
341         default:
342                 if (!btsnoop_write_file[index]) {
343                         t = tv.tv_sec;
344                         localtime_r(&t, &tm);
345
346                         if (max_index < index)
347                                 max_index = index;
348
349                         sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log",
350                                         BT_SNOOP_TYPE_HCI_PREFIX, index,
351                                         tm.tm_hour, tm.tm_min,
352                                         tm.tm_sec, tv.tv_usec);
353
354                         printf("New Index %d would be saved in %s\n", index,
355                                         write_file_name);
356
357                         btsnoop_write_file[index] = btsnoop_create(write_file_name,
358                                         BTSNOOP_FORMAT_HCI, -1, -1);
359                 }
360
361                 if (!btsnoop_write_file[index])
362                         goto close_files;
363                 btsnoop_write_hci(btsnoop_write_file[index], &tv, index,
364                                                         opcode, buf, pktlen);
365                 break;
366         }
367         num_packets++;
368
369         goto next_packet;
370
371 close_files:
372         for (index = 0; index < max_index; index++)
373                 btsnoop_unref(btsnoop_write_file[index]);
374
375         btsnoop_unref(btsnoop_read_file);
376
377         printf("BT Snoop data link transfer is completed for %lu packets\n",
378                                                                 num_packets);
379 }
380 #endif
381
382 static void command_extract_eir(const char *input)
383 {
384         struct btsnoop_pkt pkt;
385         unsigned char buf[2048];
386         ssize_t len;
387         uint32_t type, toread, flags;
388         uint16_t opcode;
389         int fd, count = 0;
390
391         fd = open_btsnoop(input, &type);
392         if (fd < 0)
393                 return;
394
395         if (type != 2001) {
396                 fprintf(stderr, "unsupported link data type %u\n", type);
397                 close(fd);
398                 return;
399         }
400
401 next_packet:
402         len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
403         if (len < 0 || len != BTSNOOP_PKT_SIZE)
404                 goto close_input;
405
406         toread = be32toh(pkt.size);
407         flags = be32toh(pkt.flags);
408
409         opcode = flags & 0x00ff;
410
411         len = read(fd, buf, toread);
412         if (len < 0 || len != (ssize_t) toread) {
413                 fprintf(stderr, "failed to read packet data\n");
414                 goto close_input;
415         }
416
417         switch (opcode) {
418         case BTSNOOP_OPCODE_EVENT_PKT:
419                 /* extended inquiry result event */
420                 if (buf[0] == 0x2f) {
421                         uint8_t *eir_ptr, eir_len, i;
422
423                         eir_len = buf[1] - 15;
424                         eir_ptr = buf + 17;
425
426                         if (eir_len < 1 || eir_len > 240)
427                                 break;
428
429                         printf("\t[Extended Inquiry Data with %u bytes]\n",
430                                                                 eir_len);
431                         printf("\t\t");
432                         for (i = 0; i < eir_len; i++) {
433                                 printf("0x%02x", eir_ptr[i]);
434                                 if (((i + 1) % 8) == 0) {
435                                         if (i < eir_len - 1)
436                                                 printf(",\n\t\t");
437                                 } else {
438                                         if (i < eir_len - 1)
439                                                 printf(", ");
440                                 }
441                         }
442                         printf("\n");
443
444                         count++;
445                 }
446                 break;
447         }
448
449         goto next_packet;
450
451 close_input:
452         close(fd);
453 }
454
455 static void command_extract_ad(const char *input)
456 {
457         struct btsnoop_pkt pkt;
458         unsigned char buf[2048];
459         ssize_t len;
460         uint32_t type, toread, flags;
461         uint16_t opcode;
462         int fd, count = 0;
463
464         fd = open_btsnoop(input, &type);
465         if (fd < 0)
466                 return;
467
468         if (type != 2001) {
469                 fprintf(stderr, "unsupported link data type %u\n", type);
470                 close(fd);
471                 return;
472         }
473
474 next_packet:
475         len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
476         if (len < 0 || len != BTSNOOP_PKT_SIZE)
477                 goto close_input;
478
479         toread = be32toh(pkt.size);
480         flags = be32toh(pkt.flags);
481
482         opcode = flags & 0x00ff;
483
484         len = read(fd, buf, toread);
485         if (len < 0 || len != (ssize_t) toread) {
486                 fprintf(stderr, "failed to read packet data\n");
487                 goto close_input;
488         }
489
490         switch (opcode) {
491         case BTSNOOP_OPCODE_EVENT_PKT:
492                 /* advertising report */
493                 if (buf[0] == 0x3e && buf[2] == 0x02) {
494                         uint8_t *ad_ptr, ad_len, i;
495
496                         ad_len = buf[12];
497                         ad_ptr = buf + 13;
498
499                         if (ad_len < 1 || ad_len > 40)
500                                 break;
501
502                         printf("\t[Advertising Data with %u bytes]\n", ad_len);
503                         printf("\t\t");
504                         for (i = 0; i < ad_len; i++) {
505                                 printf("0x%02x", ad_ptr[i]);
506                                 if (((i + 1) % 8) == 0) {
507                                         if (i < ad_len - 1)
508                                                 printf(",\n\t\t");
509                                 } else {
510                                         if (i < ad_len - 1)
511                                                 printf(", ");
512                                 }
513                         }
514                         printf("\n");
515
516                         count++;
517                 }
518                 break;
519         }
520
521         goto next_packet;
522
523 close_input:
524         close(fd);
525 }
526 static const uint8_t conn_complete[] = { 0x04, 0x03, 0x0B, 0x00 };
527 static const uint8_t disc_complete[] = { 0x04, 0x05, 0x04, 0x00 };
528
529 static void command_extract_sdp(const char *input)
530 {
531         struct btsnoop_pkt pkt;
532         unsigned char buf[2048];
533         ssize_t len;
534         uint32_t type, toread;
535         uint16_t current_cid = 0x0000;
536         uint8_t pdu_buf[512];
537         uint16_t pdu_len = 0;
538         bool pdu_first = false;
539         int fd, count = 0;
540
541         fd = open_btsnoop(input, &type);
542         if (fd < 0)
543                 return;
544
545         if (type != 1002) {
546                 fprintf(stderr, "unsupported link data type %u\n", type);
547                 close(fd);
548                 return;
549         }
550
551 next_packet:
552         len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
553         if (len < 0 || len != BTSNOOP_PKT_SIZE)
554                 goto close_input;
555
556         toread = be32toh(pkt.size);
557
558         len = read(fd, buf, toread);
559         if (len < 0 || len != (ssize_t) toread) {
560                 fprintf(stderr, "failed to read packet data\n");
561                 goto close_input;
562         }
563
564         if (buf[0] == 0x02) {
565                 uint8_t acl_flags;
566
567                 /* first 4 bytes are handle and data len */
568                 acl_flags = buf[2] >> 4;
569
570                 /* use only packet with ACL start flag */
571                 if (acl_flags & 0x02) {
572                         if (current_cid == 0x0040 && pdu_len > 0) {
573                                 int i;
574                                 if (!pdu_first)
575                                         printf(",\n");
576                                 printf("\t\traw_pdu(");
577                                 for (i = 0; i < pdu_len; i++) {
578                                         printf("0x%02x", pdu_buf[i]);
579                                         if (((i + 1) % 8) == 0) {
580                                                 if (i < pdu_len - 1)
581                                                         printf(",\n\t\t\t");
582                                         } else {
583                                                 if (i < pdu_len - 1)
584                                                         printf(", ");
585                                         }
586                                 }
587                                 printf(")");
588                                 pdu_first = false;
589                         }
590
591                         /* next 4 bytes are data len and cid */
592                         current_cid = buf[8] << 8 | buf[7];
593                         memcpy(pdu_buf, buf + 9, len - 9);
594                         pdu_len = len - 9;
595                 } else if (acl_flags & 0x01) {
596                         memcpy(pdu_buf + pdu_len, buf + 5, len - 5);
597                         pdu_len += len - 5;
598                 }
599         }
600
601         if ((size_t) len > sizeof(conn_complete)) {
602                 if (memcmp(buf, conn_complete, sizeof(conn_complete)) == 0) {
603                         printf("\tdefine_test(\"/test/%u\",\n", ++count);
604                         pdu_first = true;
605                 }
606         }
607
608         if ((size_t) len > sizeof(disc_complete)) {
609                 if (memcmp(buf, disc_complete, sizeof(disc_complete)) == 0) {
610                         printf(");\n");
611                 }
612         }
613
614         goto next_packet;
615
616 close_input:
617         close(fd);
618 }
619
620 static void usage(void)
621 {
622         printf("btsnoop trace file handling tool\n"
623                 "Usage:\n");
624         printf("\tbtsnoop <command> [files]\n");
625         printf("commands:\n"
626                 "\t-m, --merge <output>   Merge multiple btsnoop files\n"
627                 "\t-e, --extract <input>  Extract data from btsnoop file\n"
628 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
629                 "\t-s, --split <input>    Split btmon file into legacy btsnoop file(s)\n"
630 #endif
631                 "\t-h, --help             Show help options\n");
632 }
633
634 static const struct option main_options[] = {
635         { "merge",   required_argument, NULL, 'm' },
636         { "extract", required_argument, NULL, 'e' },
637         { "type",    required_argument, NULL, 't' },
638 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
639         { "split",   required_argument, NULL, 's' },
640 #endif
641         { "version", no_argument,       NULL, 'v' },
642         { "help",    no_argument,       NULL, 'h' },
643         { }
644 };
645
646 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
647 enum { INVALID, MERGE, EXTRACT, SPLIT };
648 #else
649 enum { INVALID, MERGE, EXTRACT };
650 #endif
651
652 int main(int argc, char *argv[])
653 {
654         const char *output_path = NULL;
655         const char *input_path = NULL;
656         const char *type = NULL;
657         unsigned short command = INVALID;
658
659         for (;;) {
660                 int opt;
661 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
662                 opt = getopt_long(argc, argv, "m:e:s:t:vh", main_options, NULL);
663 #else
664                 opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL);
665 #endif
666                 if (opt < 0)
667                         break;
668
669                 switch (opt) {
670                 case 'm':
671                         command = MERGE;
672                         output_path = optarg;
673                         break;
674                 case 'e':
675                         command = EXTRACT;
676                         input_path = optarg;
677                         break;
678 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
679                 case 's':
680                         command = SPLIT;
681                         input_path = optarg;
682 #endif
683                 case 't':
684                         type = optarg;
685                         break;
686                 case 'v':
687                         printf("%s\n", VERSION);
688                         return EXIT_SUCCESS;
689                 case 'h':
690                         usage();
691                         return EXIT_SUCCESS;
692                 default:
693                         return EXIT_FAILURE;
694                 }
695         }
696
697         switch (command) {
698         case MERGE:
699                 if (argc - optind < 1) {
700                         fprintf(stderr, "input files required\n");
701                         return EXIT_FAILURE;
702                 }
703
704                 command_merge(output_path, argc - optind, argv + optind);
705                 break;
706
707         case EXTRACT:
708                 if (argc - optind > 0) {
709                         fprintf(stderr, "extra arguments not allowed\n");
710                         return EXIT_FAILURE;
711                 }
712
713                 if (!type) {
714                         fprintf(stderr, "no extract type specified\n");
715                         return EXIT_FAILURE;
716                 }
717
718                 if (!strcasecmp(type, "eir"))
719                         command_extract_eir(input_path);
720                 else if (!strcasecmp(type, "ad"))
721                         command_extract_ad(input_path);
722                 else if (!strcasecmp(type, "sdp"))
723                         command_extract_sdp(input_path);
724                 else
725                         fprintf(stderr, "extract type not supported\n");
726                 break;
727
728 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
729         case SPLIT:
730                 if (argc - optind > 0) {
731                         fprintf(stderr, "extra arguments not allowed\n");
732                         return EXIT_FAILURE;
733                 }
734
735                 command_split(input_path);
736                 break;
737 #endif
738         default:
739                 usage();
740                 return EXIT_FAILURE;
741         }
742
743         return EXIT_SUCCESS;
744 }