blob: e31af35f227d8b817cb20cf40b08335ec3b2978d [file] [log] [blame]
/******************************************************************************
*
* 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)
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 = 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;
}
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;
}
}