summaryrefslogtreecommitdiffstats
path: root/hci/src/btsnoop.c
diff options
context:
space:
mode:
Diffstat (limited to 'hci/src/btsnoop.c')
-rwxr-xr-xhci/src/btsnoop.c694
1 files changed, 694 insertions, 0 deletions
diff --git a/hci/src/btsnoop.c b/hci/src/btsnoop.c
new file mode 100755
index 0000000..4807df8
--- /dev/null
+++ b/hci/src/btsnoop.c
@@ -0,0 +1,694 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+/****************************************************************************
+ *
+ * Name: btsnoopdisp.c
+ *
+ * Function: this file contains functions to generate a BTSNOOP file
+ *
+ *
+ ****************************************************************************/
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+/* for gettimeofday */
+#include <sys/time.h>
+/* for the S_* open parameters */
+#include <sys/stat.h>
+/* for write */
+#include <unistd.h>
+/* for O_* open parameters */
+#include <fcntl.h>
+/* defines the O_* open parameters */
+#include <fcntl.h>
+
+#define LOG_TAG "BTSNOOP-DISP"
+#include <cutils/log.h>
+
+#include "bt_hci_bdroid.h"
+#include "utils.h"
+
+#ifndef BTSNOOP_DBG
+#define BTSNOOP_DBG FALSE
+#endif
+
+#if (BTSNOOP_DBG == TRUE)
+#define SNOOPDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
+#else
+#define SNOOPDBG(param, ...) {}
+#endif
+
+/* file descriptor of the BT snoop file (by default, -1 means disabled) */
+int hci_btsnoop_fd = -1;
+
+/* Macro to perform a multiplication of 2 unsigned 32bit values and store the result
+ * in an unsigned 64 bit value (as two 32 bit variables):
+ * u64 = u32In1 * u32In2
+ * u32OutLow = u64[31:0]
+ * u32OutHi = u64[63:32]
+ * basically the algorithm:
+ * (hi1*2^16 + lo1)*(hi2*2^16 + lo2) = lo1*lo2 + (hi1*hi2)*2^32 + (hi1*lo2 + hi2*lo1)*2^16
+ * and the carry is propagated 16 bit by 16 bit:
+ * result[15:0] = lo1*lo2 & 0xFFFF
+ * result[31:16] = ((lo1*lo2) >> 16) + (hi1*lo2 + hi2*lo1)
+ * and so on
+ */
+#define HCIDISP_MULT_64(u32In1, u32In2, u32OutLo, u32OutHi) \
+do { \
+ uint32_t u32In1Tmp = u32In1; \
+ uint32_t u32In2Tmp = u32In2; \
+ uint32_t u32Tmp, u32Carry; \
+ u32OutLo = (u32In1Tmp & 0xFFFF) * (u32In2Tmp & 0xFFFF); /*lo1*lo2*/ \
+ u32OutHi = ((u32In1Tmp >> 16) & 0xFFFF) * ((u32In2Tmp >> 16) & 0xFFFF); /*hi1*hi2*/ \
+ u32Tmp = (u32In1Tmp & 0xFFFF) * ((u32In2Tmp >> 16) & 0xFFFF); /*lo1*hi2*/ \
+ u32Carry = (uint32_t)((u32OutLo>>16)&0xFFFF); \
+ u32Carry += (u32Tmp&0xFFFF); \
+ u32OutLo += (u32Tmp << 16) ; \
+ u32OutHi += (u32Tmp >> 16); \
+ u32Tmp = ((u32In1Tmp >> 16) & 0xFFFF) * (u32In2Tmp & 0xFFFF); \
+ u32Carry += (u32Tmp)&0xFFFF; \
+ u32Carry>>=16; \
+ u32OutLo += (u32Tmp << 16); \
+ u32OutHi += (u32Tmp >> 16); \
+ u32OutHi += u32Carry; \
+} while (0)
+
+/* Macro to make an addition of 2 64 bit values:
+ * result = (u32OutHi & u32OutLo) + (u32InHi & u32InLo)
+ * u32OutHi = result[63:32]
+ * u32OutLo = result[31:0]
+ */
+#define HCIDISP_ADD_64(u32InLo, u32InHi, u32OutLo, u32OutHi) \
+do { \
+ (u32OutLo) += (u32InLo); \
+ if ((u32OutLo) < (u32InLo)) (u32OutHi)++; \
+ (u32OutHi) += (u32InHi); \
+} while (0)
+
+/* EPOCH in microseconds since 01/01/0000 : 0x00dcddb3.0f2f8000 */
+#define BTSNOOP_EPOCH_HI 0x00dcddb3U
+#define BTSNOOP_EPOCH_LO 0x0f2f8000U
+
+/*******************************************************************************
+ **
+ ** Function tv_to_btsnoop_ts
+ **
+ ** Description This function generate a BT Snoop timestamp.
+ **
+ ** Returns void
+ **
+ ** NOTE
+ ** The return value is 64 bit as 2 32 bit variables out_lo and * out_hi.
+ ** A BT Snoop timestamp is the number of microseconds since 01/01/0000.
+ ** The timeval structure contains the number of microseconds since EPOCH
+ ** (01/01/1970) encoded as: tv.tv_sec, number of seconds since EPOCH and
+ ** tv_usec, number of microseconds in current second
+ **
+ ** Therefore the algorithm is:
+ ** result = tv.tv_sec * 1000000
+ ** result += tv.tv_usec
+ ** result += EPOCH_OFFSET
+ *******************************************************************************/
+static void tv_to_btsnoop_ts(uint32_t *out_lo, uint32_t *out_hi, struct timeval *tv)
+{
+ /* multiply the seconds by 1000000 */
+ HCIDISP_MULT_64(tv->tv_sec, 0xf4240, *out_lo, *out_hi);
+
+ /* add the microseconds */
+ HCIDISP_ADD_64((uint32_t)tv->tv_usec, 0, *out_lo, *out_hi);
+
+ /* add the epoch */
+ HCIDISP_ADD_64(BTSNOOP_EPOCH_LO, BTSNOOP_EPOCH_HI, *out_lo, *out_hi);
+}
+
+/*******************************************************************************
+ **
+ ** Function l_to_be
+ **
+ ** Description Function to convert a 32 bit value into big endian format
+ **
+ ** Returns 32 bit value in big endian format
+*******************************************************************************/
+static uint32_t l_to_be(uint32_t x)
+{
+ #if __BIG_ENDIAN != TRUE
+ x = (x >> 24) |
+ ((x >> 8) & 0xFF00) |
+ ((x << 8) & 0xFF0000) |
+ (x << 24);
+ #endif
+ return x;
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_is_open
+ **
+ ** Description Function to check if BTSNOOP is open
+ **
+ ** Returns 1 if open otherwise 0
+*******************************************************************************/
+int btsnoop_is_open(void)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ SNOOPDBG("btsnoop_is_open: snoop fd = %d\n", hci_btsnoop_fd);
+
+ if (hci_btsnoop_fd != -1)
+ {
+ return 1;
+ }
+ return 0;
+#else
+ return 2; /* Snoop not available */
+#endif
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_log_open
+ **
+ ** Description Function to open the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+static int btsnoop_log_open(char *btsnoop_logfile)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ hci_btsnoop_fd = -1;
+
+ SNOOPDBG("btsnoop_log_open: snoop log file = %s\n", btsnoop_logfile);
+
+ /* write the BT snoop header */
+ if ((btsnoop_logfile != NULL) && (strlen(btsnoop_logfile) != 0))
+ {
+ hci_btsnoop_fd = open(btsnoop_logfile, \
+ O_WRONLY|O_CREAT|O_TRUNC, \
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
+ if (hci_btsnoop_fd == -1)
+ {
+ perror("open");
+ SNOOPDBG("btsnoop_log_open: Unable to open snoop log file\n");
+ hci_btsnoop_fd = -1;
+ return 0;
+ }
+ write(hci_btsnoop_fd, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16);
+ return 1;
+ }
+#endif
+ return 2; /* Snoop not available */
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_log_close
+ **
+ ** Description Function to close the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+static int btsnoop_log_close(void)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ /* write the BT snoop header */
+ if (hci_btsnoop_fd != -1)
+ {
+ SNOOPDBG("btsnoop_log_close: Closing snoop log file\n");
+ close(hci_btsnoop_fd);
+ hci_btsnoop_fd = -1;
+ return 1;
+ }
+ return 0;
+#else
+ return 2; /* Snoop not available */
+#endif
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_hci_cmd
+ **
+ ** Description Function to add a command in the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+void btsnoop_hci_cmd(uint8_t *p)
+{
+ SNOOPDBG("btsnoop_hci_cmd: fd = %d", hci_btsnoop_fd);
+
+ if (hci_btsnoop_fd != -1)
+ {
+ uint32_t value, value_hi;
+ struct timeval tv;
+
+ /* since these display functions are called from different contexts */
+ utils_lock();
+
+ /* store the length in both original and included fields */
+ value = l_to_be(p[2] + 4);
+ write(hci_btsnoop_fd, &value, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* flags: command sent from the host */
+ value = l_to_be(2);
+ write(hci_btsnoop_fd, &value, 4);
+ /* drops: none */
+ value = 0;
+ write(hci_btsnoop_fd, &value, 4);
+ /* time */
+ gettimeofday(&tv, NULL);
+ tv_to_btsnoop_ts(&value, &value_hi, &tv);
+ value_hi = l_to_be(value_hi);
+ value = l_to_be(value);
+ write(hci_btsnoop_fd, &value_hi, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* data */
+ write(hci_btsnoop_fd, "\x1", 1);
+ write(hci_btsnoop_fd, p, p[2] + 3);
+
+ /* since these display functions are called from different contexts */
+ utils_unlock();
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_hci_evt
+ **
+ ** Description Function to add a event in the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+void btsnoop_hci_evt(uint8_t *p)
+{
+ SNOOPDBG("btsnoop_hci_evt: fd = %d", hci_btsnoop_fd);
+
+ if (hci_btsnoop_fd != -1)
+ {
+ uint32_t value, value_hi;
+ struct timeval tv;
+
+ /* since these display functions are called from different contexts */
+ utils_lock();
+
+ /* store the length in both original and included fields */
+ value = l_to_be(p[1] + 3);
+ write(hci_btsnoop_fd, &value, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* flags: event received in the host */
+ value = l_to_be(3);
+ write(hci_btsnoop_fd, &value, 4);
+ /* drops: none */
+ value = 0;
+ write(hci_btsnoop_fd, &value, 4);
+ /* time */
+ gettimeofday(&tv, NULL);
+ tv_to_btsnoop_ts(&value, &value_hi, &tv);
+ value_hi = l_to_be(value_hi);
+ value = l_to_be(value);
+ write(hci_btsnoop_fd, &value_hi, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* data */
+ write(hci_btsnoop_fd, "\x4", 1);
+ write(hci_btsnoop_fd, p, p[1] + 2);
+
+ /* since these display functions are called from different contexts */
+ utils_unlock();
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_sco_data
+ **
+ ** Description Function to add a SCO data packet in the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+void btsnoop_sco_data(uint8_t *p, uint8_t is_rcvd)
+{
+ SNOOPDBG("btsnoop_sco_data: fd = %d", hci_btsnoop_fd);
+
+ if (hci_btsnoop_fd != -1)
+ {
+ uint32_t value, value_hi;
+ struct timeval tv;
+
+ /* since these display functions are called from different contexts */
+ utils_lock();
+
+ /* store the length in both original and included fields */
+ value = l_to_be(p[2] + 4);
+ write(hci_btsnoop_fd, &value, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* flags: data can be sent or received */
+ value = l_to_be(is_rcvd?1:0);
+ write(hci_btsnoop_fd, &value, 4);
+ /* drops: none */
+ value = 0;
+ write(hci_btsnoop_fd, &value, 4);
+ /* time */
+ gettimeofday(&tv, NULL);
+ tv_to_btsnoop_ts(&value, &value_hi, &tv);
+ value_hi = l_to_be(value_hi);
+ value = l_to_be(value);
+ write(hci_btsnoop_fd, &value_hi, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* data */
+ write(hci_btsnoop_fd, "\x3", 1);
+ write(hci_btsnoop_fd, p, p[2] + 3);
+
+ /* since these display functions are called from different contexts */
+ utils_unlock();
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_acl_data
+ **
+ ** Description Function to add an ACL data packet in the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+void btsnoop_acl_data(uint8_t *p, uint8_t is_rcvd)
+{
+ SNOOPDBG("btsnoop_acl_data: fd = %d", hci_btsnoop_fd);
+ if (hci_btsnoop_fd != -1)
+ {
+ uint32_t value, value_hi;
+ struct timeval tv;
+
+ /* since these display functions are called from different contexts */
+ utils_lock();
+
+ /* store the length in both original and included fields */
+ value = l_to_be((p[3]<<8) + p[2] + 5);
+ write(hci_btsnoop_fd, &value, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* flags: data can be sent or received */
+ value = l_to_be(is_rcvd?1:0);
+ write(hci_btsnoop_fd, &value, 4);
+ /* drops: none */
+ value = 0;
+ write(hci_btsnoop_fd, &value, 4);
+ /* time */
+ gettimeofday(&tv, NULL);
+ tv_to_btsnoop_ts(&value, &value_hi, &tv);
+ value_hi = l_to_be(value_hi);
+ value = l_to_be(value);
+ write(hci_btsnoop_fd, &value_hi, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* data */
+ write(hci_btsnoop_fd, "\x2", 1);
+ write(hci_btsnoop_fd, p, (p[3]<<8) + p[2] + 4);
+
+ /* since these display functions are called from different contexts */
+ utils_unlock();
+ }
+}
+
+
+/********************************************************************************
+ ** API allow external realtime parsing of output using e.g hcidump
+ *********************************************************************************/
+
+#define EXT_PARSER_PORT 4330
+
+static pthread_t thread_id;
+static int s_listen = -1;
+static int ext_parser_fd = -1;
+
+static void ext_parser_detached(void);
+
+static int ext_parser_accept(int port)
+{
+ socklen_t clilen;
+ struct sockaddr_in cliaddr, servaddr;
+ int s, srvlen;
+ int n = 1;
+ int size_n;
+ int result = 0;
+
+ ALOGD("waiting for connection on port %d", port);
+
+ s_listen = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (s_listen < 0)
+ {
+ ALOGE("listener not created: listen fd %d", s_listen);
+ return -1;
+ }
+
+ bzero(&servaddr, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ servaddr.sin_port = htons(port);
+
+ srvlen = sizeof(servaddr);
+
+ /* allow reuse of sock addr upon bind */
+ result = setsockopt(s_listen, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if (result<0)
+ {
+ perror("setsockopt");
+ }
+
+ result = bind(s_listen, (struct sockaddr *) &servaddr, srvlen);
+
+ if (result < 0)
+ perror("bind");
+
+ result = listen(s_listen, 1);
+
+ if (result < 0)
+ perror("listen");
+
+ clilen = sizeof(struct sockaddr_in);
+
+ s = accept(s_listen, (struct sockaddr *) &cliaddr, &clilen);
+
+ if (s < 0)
+{
+ perror("accept");
+ return -1;
+ }
+
+ ALOGD("connected (%d)", s);
+
+ return s;
+}
+
+static int send_ext_parser(char *p, int len)
+{
+ int n;
+
+ /* check if io socket is connected */
+ if (ext_parser_fd == -1)
+ return 0;
+
+ SNOOPDBG("write %d to snoop socket\n", len);
+
+ n = write(ext_parser_fd, p, len);
+
+ if (n<=0)
+ {
+ ext_parser_detached();
+ }
+
+ return n;
+}
+
+static void ext_parser_detached(void)
+{
+ ALOGD("ext parser detached");
+
+ if (ext_parser_fd>0)
+ close(ext_parser_fd);
+
+ if (s_listen > 0)
+ close(s_listen);
+
+ ext_parser_fd = -1;
+ s_listen = -1;
+}
+
+static void interruptFn (int sig)
+{
+ ALOGD("interruptFn");
+ pthread_exit(0);
+}
+
+static void ext_parser_thread(void* param)
+{
+ int fd;
+ int sig = SIGUSR2;
+ sigset_t sigSet;
+ sigemptyset (&sigSet);
+ sigaddset (&sigSet, sig);
+
+ ALOGD("ext_parser_thread");
+
+ prctl(PR_SET_NAME, (unsigned long)"BtsnoopExtParser", 0, 0, 0);
+
+ pthread_sigmask (SIG_UNBLOCK, &sigSet, NULL);
+
+ struct sigaction act;
+ act.sa_handler = interruptFn;
+ sigaction (sig, &act, NULL );
+
+ do
+ {
+ fd = ext_parser_accept(EXT_PARSER_PORT);
+
+ ext_parser_fd = fd;
+
+ ALOGD("ext parser attached on fd %d\n", ext_parser_fd);
+ } while (1);
+}
+
+void btsnoop_stop_listener(void)
+{
+ ALOGD("btsnoop_init");
+ ext_parser_detached();
+}
+
+void btsnoop_init(void)
+{
+#if defined(BTSNOOP_EXT_PARSER_INCLUDED) && (BTSNOOP_EXT_PARSER_INCLUDED == TRUE)
+ ALOGD("btsnoop_init");
+
+ /* always setup ext listener port */
+ if (pthread_create(&thread_id, NULL,
+ (void*)ext_parser_thread,NULL)!=0)
+ perror("pthread_create");
+#endif
+}
+
+void btsnoop_open(char *p_path)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ ALOGD("btsnoop_open");
+ btsnoop_log_open(p_path);
+#endif // BTSNOOPDISP_INCLUDED
+}
+
+void btsnoop_close(void)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ ALOGD("btsnoop_close");
+ btsnoop_log_close();
+#endif
+}
+
+void btsnoop_cleanup (void)
+{
+#if defined(BTSNOOP_EXT_PARSER_INCLUDED) && (BTSNOOP_EXT_PARSER_INCLUDED == TRUE)
+ ALOGD("btsnoop_cleanup");
+ pthread_kill(thread_id, SIGUSR2);
+ pthread_join(thread_id, NULL);
+ ext_parser_detached();
+#endif
+}
+
+
+#define HCIT_TYPE_COMMAND 1
+#define HCIT_TYPE_ACL_DATA 2
+#define HCIT_TYPE_SCO_DATA 3
+#define HCIT_TYPE_EVENT 4
+
+void btsnoop_capture(HC_BT_HDR *p_buf, uint8_t is_rcvd)
+{
+ uint8_t *p = (uint8_t *)(p_buf + 1) + p_buf->offset;
+
+ SNOOPDBG("btsnoop_capture: fd = %d, type %x, rcvd %d, ext %d", \
+ hci_btsnoop_fd, p_buf->event, is_rcvd, ext_parser_fd);
+
+#if defined(BTSNOOP_EXT_PARSER_INCLUDED) && (BTSNOOP_EXT_PARSER_INCLUDED == TRUE)
+ if (ext_parser_fd > 0)
+ {
+ uint8_t tmp = *p;
+
+ /* borrow one byte for H4 packet type indicator */
+ p--;
+
+ switch (p_buf->event & MSG_EVT_MASK)
+ {
+ case MSG_HC_TO_STACK_HCI_EVT:
+ *p = HCIT_TYPE_EVENT;
+ break;
+ case MSG_HC_TO_STACK_HCI_ACL:
+ case MSG_STACK_TO_HC_HCI_ACL:
+ *p = HCIT_TYPE_ACL_DATA;
+ break;
+ case MSG_HC_TO_STACK_HCI_SCO:
+ case MSG_STACK_TO_HC_HCI_SCO:
+ *p = HCIT_TYPE_SCO_DATA;
+ break;
+ case MSG_STACK_TO_HC_HCI_CMD:
+ *p = HCIT_TYPE_COMMAND;
+ break;
+ }
+
+ send_ext_parser((char*)p, p_buf->len+1);
+ *(++p) = tmp;
+ return;
+ }
+#endif
+
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ if (hci_btsnoop_fd == -1)
+ return;
+
+ switch (p_buf->event & MSG_EVT_MASK)
+ {
+ case MSG_HC_TO_STACK_HCI_EVT:
+ SNOOPDBG("TYPE : EVT");
+ btsnoop_hci_evt(p);
+ break;
+ case MSG_HC_TO_STACK_HCI_ACL:
+ case MSG_STACK_TO_HC_HCI_ACL:
+ SNOOPDBG("TYPE : ACL");
+ btsnoop_acl_data(p, is_rcvd);
+ break;
+ case MSG_HC_TO_STACK_HCI_SCO:
+ case MSG_STACK_TO_HC_HCI_SCO:
+ SNOOPDBG("TYPE : SCO");
+ btsnoop_sco_data(p, is_rcvd);
+ break;
+ case MSG_STACK_TO_HC_HCI_CMD:
+ SNOOPDBG("TYPE : CMD");
+ btsnoop_hci_cmd(p);
+ break;
+ }
+#endif // BTSNOOPDISP_INCLUDED
+}
+
+