| /****************************************************************************** |
| * |
| * Copyright (C) 2009-2012 Broadcom Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| ******************************************************************************/ |
| |
| #define LOG_TAG "btsnoop" |
| |
| #include <arpa/inet.h> |
| #include <assert.h> |
| #include <ctype.h> |
| #include <cutils/log.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <unistd.h> |
| |
| #include "bt_hci_bdroid.h" |
| #include "bt_utils.h" |
| #include "utils.h" |
| |
| typedef enum { |
| kCommandPacket = 1, |
| kAclPacket = 2, |
| kScoPacket = 3, |
| kEventPacket = 4 |
| } packet_type_t; |
| |
| // Epoch in microseconds since 01/01/0000. |
| static const uint64_t BTSNOOP_EPOCH_DELTA = 0x00dcddb30f2f8000ULL; |
| |
| // File descriptor for btsnoop file. |
| static int hci_btsnoop_fd = -1; |
| |
| void btsnoop_net_open(); |
| void btsnoop_net_close(); |
| void btsnoop_net_write(const void *data, size_t length); |
| |
| static uint64_t btsnoop_timestamp(void) { |
| struct timeval tv; |
| gettimeofday(&tv, NULL); |
| |
| // Timestamp is in microseconds. |
| uint64_t timestamp = tv.tv_sec * 1000 * 1000LL; |
| timestamp += tv.tv_usec; |
| timestamp += BTSNOOP_EPOCH_DELTA; |
| return timestamp; |
| } |
| |
| static void btsnoop_write(const void *data, size_t length) { |
| if (hci_btsnoop_fd != -1) |
| TEMP_FAILURE_RETRY(write(hci_btsnoop_fd, data, length)); |
| |
| btsnoop_net_write(data, length); |
| } |
| |
| static void btsnoop_write_packet(packet_type_t type, const uint8_t *packet, bool is_received) { |
| int length_he = 0; |
| int length; |
| int flags; |
| int drops = 0; |
| switch (type) { |
| case kCommandPacket: |
| length_he = packet[2] + 4; |
| flags = 2; |
| break; |
| case kAclPacket: |
| length_he = (packet[3] << 8) + packet[2] + 5; |
| flags = is_received; |
| break; |
| case kScoPacket: |
| length_he = packet[2] + 4; |
| flags = is_received; |
| break; |
| case kEventPacket: |
| length_he = packet[1] + 3; |
| flags = 3; |
| break; |
| } |
| |
| uint64_t timestamp = btsnoop_timestamp(); |
| uint32_t time_hi = timestamp >> 32; |
| uint32_t time_lo = timestamp & 0xFFFFFFFF; |
| |
| length = htonl(length_he); |
| flags = htonl(flags); |
| drops = htonl(drops); |
| time_hi = htonl(time_hi); |
| time_lo = htonl(time_lo); |
| |
| // This function is called from different contexts. |
| utils_lock(); |
| |
| btsnoop_write(&length, 4); |
| btsnoop_write(&length, 4); |
| btsnoop_write(&flags, 4); |
| btsnoop_write(&drops, 4); |
| btsnoop_write(&time_hi, 4); |
| btsnoop_write(&time_lo, 4); |
| btsnoop_write(&type, 1); |
| btsnoop_write(packet, length_he - 1); |
| |
| utils_unlock(); |
| } |
| |
| void btsnoop_open(const char *p_path, const bool save_existing) { |
| assert(p_path != NULL); |
| assert(*p_path != '\0'); |
| |
| btsnoop_net_open(); |
| |
| if (hci_btsnoop_fd != -1) { |
| ALOGE("%s btsnoop log file is already open.", __func__); |
| return; |
| } |
| |
| if (save_existing) |
| { |
| char fname_backup[266] = {0}; |
| strncat(fname_backup, p_path, 255); |
| strcat(fname_backup, ".last"); |
| rename(p_path, fname_backup); |
| } |
| |
| hci_btsnoop_fd = TEMP_FAILURE_RETRY(open(p_path, |
| O_WRONLY | O_CREAT | O_TRUNC, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)); |
| |
| if (hci_btsnoop_fd == -1) { |
| ALOGE("%s unable to open '%s': %s", __func__, p_path, strerror(errno)); |
| return; |
| } |
| |
| TEMP_FAILURE_RETRY(write(hci_btsnoop_fd, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16)); |
| } |
| |
| void btsnoop_close(void) { |
| if (hci_btsnoop_fd != -1) |
| close(hci_btsnoop_fd); |
| hci_btsnoop_fd = -1; |
| |
| btsnoop_net_close(); |
| } |
| |
| void btsnoop_capture(const HC_BT_HDR *p_buf, bool is_rcvd) { |
| const uint8_t *p = (const uint8_t *)(p_buf + 1) + p_buf->offset; |
| |
| if (hci_btsnoop_fd == -1) |
| return; |
| |
| switch (p_buf->event & MSG_EVT_MASK) { |
| case MSG_HC_TO_STACK_HCI_EVT: |
| btsnoop_write_packet(kEventPacket, p, false); |
| break; |
| case MSG_HC_TO_STACK_HCI_ACL: |
| case MSG_STACK_TO_HC_HCI_ACL: |
| btsnoop_write_packet(kAclPacket, p, is_rcvd); |
| break; |
| case MSG_HC_TO_STACK_HCI_SCO: |
| case MSG_STACK_TO_HC_HCI_SCO: |
| btsnoop_write_packet(kScoPacket, p, is_rcvd); |
| break; |
| case MSG_STACK_TO_HC_HCI_CMD: |
| btsnoop_write_packet(kCommandPacket, p, true); |
| break; |
| } |
| } |