3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2011-2012 Intel Corporation
6 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
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.
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.
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
37 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
43 #include <arpa/inet.h>
46 #include "src/shared/btsnoop.h"
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))
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))
65 static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
66 0x6f, 0x6f, 0x70, 0x00 };
68 static const uint32_t btsnoop_version = 1;
70 static int create_btsnoop(const char *path)
72 struct btsnoop_hdr hdr;
76 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
77 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
79 perror("failed to output file");
83 memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
84 hdr.version = htobe32(btsnoop_version);
85 hdr.type = htobe32(2001);
87 written = write(fd, &hdr, BTSNOOP_HDR_SIZE);
89 perror("failed to write output header");
97 static int open_btsnoop(const char *path, uint32_t *type)
99 struct btsnoop_hdr hdr;
103 fd = open(path, O_RDONLY | O_CLOEXEC);
105 perror("failed to open input file");
109 len = read(fd, &hdr, BTSNOOP_HDR_SIZE);
110 if (len < 0 || len != BTSNOOP_HDR_SIZE) {
111 perror("failed to read input header");
116 if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) {
117 fprintf(stderr, "not a valid btsnoop header\n");
122 if (be32toh(hdr.version) != btsnoop_version) {
123 fprintf(stderr, "invalid btsnoop version\n");
129 *type = be32toh(hdr.type);
136 static void command_merge(const char *output, int argc, char *argv[])
138 struct btsnoop_pkt input_pkt[MAX_MERGE];
139 unsigned char buf[2048];
140 int output_fd, input_fd[MAX_MERGE], num_input = 0;
142 ssize_t len, written;
143 uint32_t toread, flags;
144 uint16_t index, opcode;
146 if (argc > MAX_MERGE) {
147 fprintf(stderr, "only up to %d files allowed\n", MAX_MERGE);
151 for (i = 0; i < argc; i++) {
155 fd = open_btsnoop(argv[i], &type);
160 fprintf(stderr, "unsupported link data type %u\n",
166 input_fd[num_input++] = fd;
169 if (num_input != argc) {
170 fprintf(stderr, "failed to open all input files\n");
174 output_fd = create_btsnoop(output);
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) {
189 for (i = 0; i < num_input; i++) {
195 if (select_input < 0) {
200 ts = be64toh(input_pkt[i].ts);
202 if (ts < be64toh(input_pkt[select_input].ts))
206 if (select_input < 0)
209 toread = be32toh(input_pkt[select_input].size);
210 flags = be32toh(input_pkt[select_input].flags);
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;
219 written = htobe32(toread - 1);
220 input_pkt[select_input].size = written;
221 input_pkt[select_input].len = written;
225 opcode = BTSNOOP_OPCODE_COMMAND_PKT;
229 opcode = BTSNOOP_OPCODE_ACL_RX_PKT;
231 opcode = BTSNOOP_OPCODE_ACL_TX_PKT;
235 opcode = BTSNOOP_OPCODE_SCO_RX_PKT;
237 opcode = BTSNOOP_OPCODE_SCO_TX_PKT;
240 opcode = BTSNOOP_OPCODE_EVENT_PKT;
246 index = select_input;
247 input_pkt[select_input].flags = htobe32((index << 16) | opcode);
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");
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");
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;
275 for (i = 0; i < num_input; i++)
278 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
279 #define BT_SNOOP_TYPE_HCI_PREFIX "btsnoop_type_hci"
281 static void command_split(const char *input)
283 unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
284 uint16_t pktlen,opcode;
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];
293 unsigned long num_packets = 0;
295 btsnoop_read_file = btsnoop_open(input, BTSNOOP_FLAG_PKLG_SUPPORT);
296 if (!btsnoop_read_file)
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);
307 if (!btsnoop_read_hci(btsnoop_read_file, &tv, &index, &opcode, buf,
311 if (opcode == 0xffff)
315 case BTSNOOP_OPCODE_NEW_INDEX:
317 localtime_r(&t, &tm);
319 if (max_index < index)
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);
326 printf("New Index %d would be saved in %s\n", index,
329 btsnoop_write_file[index] = btsnoop_create(write_file_name,
330 BTSNOOP_FORMAT_HCI, -1, -1);
331 if (!btsnoop_write_file[index])
335 case BTSNOOP_OPCODE_DEL_INDEX:
336 printf("Del Index %d\n", index);
338 btsnoop_unref(btsnoop_write_file[index]);
339 btsnoop_write_file[index] = NULL;
342 if (!btsnoop_write_file[index]) {
344 localtime_r(&t, &tm);
346 if (max_index < index)
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);
354 printf("New Index %d would be saved in %s\n", index,
357 btsnoop_write_file[index] = btsnoop_create(write_file_name,
358 BTSNOOP_FORMAT_HCI, -1, -1);
361 if (!btsnoop_write_file[index])
363 btsnoop_write_hci(btsnoop_write_file[index], &tv, index,
364 opcode, buf, pktlen);
372 for (index = 0; index < max_index; index++)
373 btsnoop_unref(btsnoop_write_file[index]);
375 btsnoop_unref(btsnoop_read_file);
377 printf("BT Snoop data link transfer is completed for %lu packets\n",
382 static void command_extract_eir(const char *input)
384 struct btsnoop_pkt pkt;
385 unsigned char buf[2048];
387 uint32_t type, toread, flags;
391 fd = open_btsnoop(input, &type);
396 fprintf(stderr, "unsupported link data type %u\n", type);
402 len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
403 if (len < 0 || len != BTSNOOP_PKT_SIZE)
406 toread = be32toh(pkt.size);
407 flags = be32toh(pkt.flags);
409 opcode = flags & 0x00ff;
411 len = read(fd, buf, toread);
412 if (len < 0 || len != (ssize_t) toread) {
413 fprintf(stderr, "failed to read packet data\n");
418 case BTSNOOP_OPCODE_EVENT_PKT:
419 /* extended inquiry result event */
420 if (buf[0] == 0x2f) {
421 uint8_t *eir_ptr, eir_len, i;
423 eir_len = buf[1] - 15;
426 if (eir_len < 1 || eir_len > 240)
429 printf("\t[Extended Inquiry Data with %u bytes]\n",
432 for (i = 0; i < eir_len; i++) {
433 printf("0x%02x", eir_ptr[i]);
434 if (((i + 1) % 8) == 0) {
455 static void command_extract_ad(const char *input)
457 struct btsnoop_pkt pkt;
458 unsigned char buf[2048];
460 uint32_t type, toread, flags;
464 fd = open_btsnoop(input, &type);
469 fprintf(stderr, "unsupported link data type %u\n", type);
475 len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
476 if (len < 0 || len != BTSNOOP_PKT_SIZE)
479 toread = be32toh(pkt.size);
480 flags = be32toh(pkt.flags);
482 opcode = flags & 0x00ff;
484 len = read(fd, buf, toread);
485 if (len < 0 || len != (ssize_t) toread) {
486 fprintf(stderr, "failed to read packet data\n");
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;
499 if (ad_len < 1 || ad_len > 40)
502 printf("\t[Advertising Data with %u bytes]\n", ad_len);
504 for (i = 0; i < ad_len; i++) {
505 printf("0x%02x", ad_ptr[i]);
506 if (((i + 1) % 8) == 0) {
526 static const uint8_t conn_complete[] = { 0x04, 0x03, 0x0B, 0x00 };
527 static const uint8_t disc_complete[] = { 0x04, 0x05, 0x04, 0x00 };
529 static void command_extract_sdp(const char *input)
531 struct btsnoop_pkt pkt;
532 unsigned char buf[2048];
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;
541 fd = open_btsnoop(input, &type);
546 fprintf(stderr, "unsupported link data type %u\n", type);
552 len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
553 if (len < 0 || len != BTSNOOP_PKT_SIZE)
556 toread = be32toh(pkt.size);
558 len = read(fd, buf, toread);
559 if (len < 0 || len != (ssize_t) toread) {
560 fprintf(stderr, "failed to read packet data\n");
564 if (buf[0] == 0x02) {
567 /* first 4 bytes are handle and data len */
568 acl_flags = buf[2] >> 4;
570 /* use only packet with ACL start flag */
571 if (acl_flags & 0x02) {
572 if (current_cid == 0x0040 && pdu_len > 0) {
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) {
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);
595 } else if (acl_flags & 0x01) {
596 memcpy(pdu_buf + pdu_len, buf + 5, len - 5);
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);
608 if ((size_t) len > sizeof(disc_complete)) {
609 if (memcmp(buf, disc_complete, sizeof(disc_complete)) == 0) {
620 static void usage(void)
622 printf("btsnoop trace file handling tool\n"
624 printf("\tbtsnoop <command> [files]\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"
631 "\t-h, --help Show help options\n");
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' },
641 { "version", no_argument, NULL, 'v' },
642 { "help", no_argument, NULL, 'h' },
646 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
647 enum { INVALID, MERGE, EXTRACT, SPLIT };
649 enum { INVALID, MERGE, EXTRACT };
652 int main(int argc, char *argv[])
654 const char *output_path = NULL;
655 const char *input_path = NULL;
656 const char *type = NULL;
657 unsigned short command = INVALID;
661 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
662 opt = getopt_long(argc, argv, "m:e:s:t:vh", main_options, NULL);
664 opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL);
672 output_path = optarg;
678 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
687 printf("%s\n", VERSION);
699 if (argc - optind < 1) {
700 fprintf(stderr, "input files required\n");
704 command_merge(output_path, argc - optind, argv + optind);
708 if (argc - optind > 0) {
709 fprintf(stderr, "extra arguments not allowed\n");
714 fprintf(stderr, "no extract type specified\n");
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);
725 fprintf(stderr, "extract type not supported\n");
728 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
730 if (argc - optind > 0) {
731 fprintf(stderr, "extra arguments not allowed\n");
735 command_split(input_path);