tizen 2.3.1 release
[framework/connectivity/bluez.git] / monitor / control.c
index 159ba9d..44cc1da 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  This library is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 #endif
 
 #include <stdio.h>
+#include <stdbool.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/mgmt.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/mgmt.h"
 
-#include "mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/btsnoop.h"
+#include "src/shared/mainloop.h"
+
+#include "display.h"
 #include "packet.h"
+#include "hcidump.h"
+#include "ellisys.h"
 #include "control.h"
 
+static struct btsnoop *btsnoop_file = NULL;
+static bool hcidump_fallback = false;
+
 struct control_data {
        uint16_t channel;
        int fd;
+       unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
+       uint16_t offset;
 };
 
 static void free_data(void *user_data)
@@ -69,6 +83,20 @@ static void mgmt_index_removed(uint16_t len, const void *buf)
        packet_hexdump(buf, len);
 }
 
+static void mgmt_unconf_index_added(uint16_t len, const void *buf)
+{
+       printf("@ Unconfigured Index Added\n");
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_unconf_index_removed(uint16_t len, const void *buf)
+{
+       printf("@ Unconfigured Index Removed\n");
+
+       packet_hexdump(buf, len);
+}
+
 static void mgmt_controller_error(uint16_t len, const void *buf)
 {
        const struct mgmt_ev_controller_error *ev = buf;
@@ -90,9 +118,44 @@ static void mgmt_controller_error(uint16_t len, const void *buf)
 #define NELEM(x) (sizeof(x) / sizeof((x)[0]))
 #endif
 
+static const char *config_options_str[] = {
+       "external", "public-address",
+};
+
+static void mgmt_new_config_options(uint16_t len, const void *buf)
+{
+       uint32_t options;
+       unsigned int i;
+
+       if (len < 4) {
+               printf("* Malformed New Configuration Options control\n");
+               return;
+       }
+
+       options = get_le32(buf);
+
+       printf("@ New Configuration Options: 0x%4.4x\n", options);
+
+       if (options) {
+               printf("%-12c", ' ');
+               for (i = 0; i < NELEM(config_options_str); i++) {
+                       if (options & (1 << i))
+                               printf("%s ", config_options_str[i]);
+               }
+               printf("\n");
+       }
+
+       buf += 4;
+       len -= 4;
+
+       packet_hexdump(buf, len);
+}
+
 static const char *settings_str[] = {
        "powered", "connectable", "fast-connectable", "discoverable",
-       "pairable", "link-security", "ssp", "br/edr", "hs", "le"
+       "bondable", "link-security", "ssp", "br/edr", "hs", "le",
+       "advertising", "secure-conn", "debug-keys", "privacy",
+       "configuration", "static-addr",
 };
 
 static void mgmt_new_settings(uint16_t len, const void *buf)
@@ -105,16 +168,18 @@ static void mgmt_new_settings(uint16_t len, const void *buf)
                return;
        }
 
-       settings = bt_get_le32(buf);
+       settings = get_le32(buf);
 
        printf("@ New Settings: 0x%4.4x\n", settings);
 
-       printf("%-12c", ' ');
-       for (i = 0; i < NELEM(settings_str); i++) {
-               if (settings & (1 << i))
-                       printf("%s ", settings_str[i]);
+       if (settings) {
+               printf("%-12c", ' ');
+               for (i = 0; i < NELEM(settings_str); i++) {
+                       if (settings & (1 << i))
+                               printf("%s ", settings_str[i]);
+               }
+               printf("\n");
        }
-       printf("\n");
 
        buf += 4;
        len -= 4;
@@ -182,6 +247,7 @@ static void mgmt_new_link_key(uint16_t len, const void *buf)
 static void mgmt_new_long_term_key(uint16_t len, const void *buf)
 {
        const struct mgmt_ev_new_long_term_key *ev = buf;
+       const char *type;
        char str[18];
 
        if (len < sizeof(*ev)) {
@@ -189,9 +255,38 @@ static void mgmt_new_long_term_key(uint16_t len, const void *buf)
                return;
        }
 
+       /* LE SC keys are both for master and slave */
+       switch (ev->key.type) {
+       case 0x00:
+               if (ev->key.master)
+                       type = "Master (Unauthenticated)";
+               else
+                       type = "Slave (Unauthenticated)";
+               break;
+       case 0x01:
+               if (ev->key.master)
+                       type = "Master (Authenticated)";
+               else
+                       type = "Slave (Authenticated)";
+               break;
+       case 0x02:
+               type = "SC (Unauthenticated)";
+               break;
+       case 0x03:
+               type = "SC (Authenticated)";
+               break;
+       case 0x04:
+               type = "SC (Debug)";
+               break;
+       default:
+               type = "<unknown>";
+               break;
+       }
+
        ba2str(&ev->key.addr.bdaddr, str);
 
-       printf("@ New Long Term Key: %s (%d)\n", str, ev->key.addr.type);
+       printf("@ New Long Term Key: %s (%d) %s 0x%02x\n", str,
+                       ev->key.addr.type, type, ev->key.type);
 
        buf += sizeof(*ev);
        len -= sizeof(*ev);
@@ -210,7 +305,7 @@ static void mgmt_device_connected(uint16_t len, const void *buf)
                return;
        }
 
-       flags = btohs(ev->flags);
+       flags = le32_to_cpu(ev->flags);
        ba2str(&ev->addr.bdaddr, str);
 
        printf("@ Device Connected: %s (%d) flags 0x%4.4x\n",
@@ -226,18 +321,29 @@ static void mgmt_device_disconnected(uint16_t len, const void *buf)
 {
        const struct mgmt_ev_device_disconnected *ev = buf;
        char str[18];
+       uint8_t reason;
+       uint16_t consumed_len;
 
-       if (len < sizeof(*ev)) {
+       if (len < sizeof(struct mgmt_addr_info)) {
                printf("* Malformed Device Disconnected control\n");
                return;
        }
 
+       if (len < sizeof(*ev)) {
+               reason = MGMT_DEV_DISCONN_UNKNOWN;
+               consumed_len = len;
+       } else {
+               reason = ev->reason;
+               consumed_len = sizeof(*ev);
+       }
+
        ba2str(&ev->addr.bdaddr, str);
 
-       printf("@ Device Disconnected: %s (%d)\n", str, ev->addr.type);
+       printf("@ Device Disconnected: %s (%d) reason %u\n", str, ev->addr.type,
+                                                                       reason);
 
-       buf += sizeof(*ev);
-       len -= sizeof(*ev);
+       buf += consumed_len;
+       len -= consumed_len;
 
        packet_hexdump(buf, len);
 }
@@ -317,7 +423,7 @@ static void mgmt_user_passkey_request(uint16_t len, const void *buf)
 
        ba2str(&ev->addr.bdaddr, str);
 
-       printf("@ PIN User Passkey Request: %s (%d)\n", str, ev->addr.type);
+       printf("@ User Passkey Request: %s (%d)\n", str, ev->addr.type);
 
        buf += sizeof(*ev);
        len -= sizeof(*ev);
@@ -357,7 +463,7 @@ static void mgmt_device_found(uint16_t len, const void *buf)
                return;
        }
 
-       flags = btohs(ev->flags);
+       flags = le32_to_cpu(ev->flags);
        ba2str(&ev->addr.bdaddr, str);
 
        printf("@ Device Found: %s (%d) rssi %d flags 0x%4.4x\n",
@@ -446,6 +552,158 @@ static void mgmt_device_unpaired(uint16_t len, const void *buf)
        packet_hexdump(buf, len);
 }
 
+static void mgmt_passkey_notify(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_passkey_notify *ev = buf;
+       uint32_t passkey;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Passkey Notify control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       passkey = le32_to_cpu(ev->passkey);
+
+       printf("@ Passkey Notify: %s (%d) passkey %06u entered %u\n",
+                               str, ev->addr.type, passkey, ev->entered);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_new_irk(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_new_irk *ev = buf;
+       char addr[18], rpa[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed New IRK control\n");
+               return;
+       }
+
+       ba2str(&ev->rpa, rpa);
+       ba2str(&ev->key.addr.bdaddr, addr);
+
+       printf("@ New IRK: %s (%d) %s\n", addr, ev->key.addr.type, rpa);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_new_csrk(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_new_csrk *ev = buf;
+       const char *type;
+       char addr[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed New CSRK control\n");
+               return;
+       }
+
+       ba2str(&ev->key.addr.bdaddr, addr);
+
+       switch (ev->key.type) {
+       case 0x00:
+               type = "Local Unauthenticated";
+               break;
+       case 0x01:
+               type = "Remote Unauthenticated";
+               break;
+       case 0x02:
+               type = "Local Authenticated";
+               break;
+       case 0x03:
+               type = "Remote Authenticated";
+               break;
+       default:
+               type = "<unknown>";
+               break;
+       }
+
+       printf("@ New CSRK: %s (%d) %s (%u)\n", addr, ev->key.addr.type,
+                                                       type, ev->key.type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_device_added(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_device_added *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Device Added control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Device Added: %s (%d) %d\n", str, ev->addr.type, ev->action);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_device_removed(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_device_removed *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Device Removed control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Device Removed: %s (%d)\n", str, ev->addr.type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_new_conn_param(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_new_conn_param *ev = buf;
+       char addr[18];
+       uint16_t min, max, latency, timeout;
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed New Connection Parameter control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+       min = le16_to_cpu(ev->min_interval);
+       max = le16_to_cpu(ev->max_interval);
+       latency = le16_to_cpu(ev->latency);
+       timeout = le16_to_cpu(ev->timeout);
+
+       printf("@ New Conn Param: %s (%d) hint %d min 0x%4.4x max 0x%4.4x "
+               "latency 0x%4.4x timeout 0x%4.4x\n", addr, ev->addr.type,
+               ev->store_hint, min, max, latency, timeout);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
 void control_message(uint16_t opcode, const void *data, uint16_t size)
 {
        switch (opcode) {
@@ -509,6 +767,33 @@ void control_message(uint16_t opcode, const void *data, uint16_t size)
        case MGMT_EV_DEVICE_UNPAIRED:
                mgmt_device_unpaired(size, data);
                break;
+       case MGMT_EV_PASSKEY_NOTIFY:
+               mgmt_passkey_notify(size, data);
+               break;
+       case MGMT_EV_NEW_IRK:
+               mgmt_new_irk(size, data);
+               break;
+       case MGMT_EV_NEW_CSRK:
+               mgmt_new_csrk(size, data);
+               break;
+       case MGMT_EV_DEVICE_ADDED:
+               mgmt_device_added(size, data);
+               break;
+       case MGMT_EV_DEVICE_REMOVED:
+               mgmt_device_removed(size, data);
+               break;
+       case MGMT_EV_NEW_CONN_PARAM:
+               mgmt_new_conn_param(size, data);
+               break;
+       case MGMT_EV_UNCONF_INDEX_ADDED:
+               mgmt_unconf_index_added(size, data);
+               break;
+       case MGMT_EV_UNCONF_INDEX_REMOVED:
+               mgmt_unconf_index_removed(size, data);
+               break;
+       case MGMT_EV_NEW_CONFIG_OPTIONS:
+               mgmt_new_config_options(size, data);
+               break;
        default:
                printf("* Unknown control (code %d len %d)\n", opcode, size);
                packet_hexdump(data, size);
@@ -519,21 +804,20 @@ void control_message(uint16_t opcode, const void *data, uint16_t size)
 static void data_callback(int fd, uint32_t events, void *user_data)
 {
        struct control_data *data = user_data;
-       unsigned char buf[HCI_MAX_FRAME_SIZE];
        unsigned char control[32];
        struct mgmt_hdr hdr;
        struct msghdr msg;
        struct iovec iov[2];
 
        if (events & (EPOLLERR | EPOLLHUP)) {
-               mainloop_remove_fd(fd);
+               mainloop_remove_fd(data->fd);
                return;
        }
 
        iov[0].iov_base = &hdr;
        iov[0].iov_len = MGMT_HDR_SIZE;
-       iov[1].iov_base = buf;
-       iov[1].iov_len = sizeof(buf);
+       iov[1].iov_base = data->buf;
+       iov[1].iov_len = sizeof(data->buf);
 
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = iov;
@@ -544,10 +828,11 @@ static void data_callback(int fd, uint32_t events, void *user_data)
        while (1) {
                struct cmsghdr *cmsg;
                struct timeval *tv = NULL;
+               struct timeval ctv;
                uint16_t opcode, index, pktlen;
                ssize_t len;
 
-               len = recvmsg(fd, &msg, MSG_DONTWAIT);
+               len = recvmsg(data->fd, &msg, MSG_DONTWAIT);
                if (len < 0)
                        break;
 
@@ -559,20 +844,26 @@ static void data_callback(int fd, uint32_t events, void *user_data)
                        if (cmsg->cmsg_level != SOL_SOCKET)
                                continue;
 
-                       if (cmsg->cmsg_type == SCM_TIMESTAMP)
-                               tv = (struct timeval *) CMSG_DATA(cmsg);
+                       if (cmsg->cmsg_type == SCM_TIMESTAMP) {
+                               memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
+                               tv = &ctv;
+                       }
                }
 
-               opcode = btohs(hdr.opcode);
-               index  = btohs(hdr.index);
-               pktlen = btohs(hdr.len);
+               opcode = le16_to_cpu(hdr.opcode);
+               index  = le16_to_cpu(hdr.index);
+               pktlen = le16_to_cpu(hdr.len);
 
                switch (data->channel) {
                case HCI_CHANNEL_CONTROL:
-                       packet_control(tv, index, opcode, buf, pktlen);
+                       packet_control(tv, index, opcode, data->buf, pktlen);
                        break;
                case HCI_CHANNEL_MONITOR:
-                       packet_monitor(tv, index, opcode, buf, pktlen);
+                       btsnoop_write_hci(btsnoop_file, tv, index, opcode,
+                                                       data->buf, pktlen);
+                       ellisys_inject_hci(tv, index, opcode,
+                                                       data->buf, pktlen);
+                       packet_monitor(tv, index, opcode, data->buf, pktlen);
                        break;
                }
        }
@@ -583,7 +874,7 @@ static int open_socket(uint16_t channel)
        struct sockaddr_hci addr;
        int fd, opt = 1;
 
-       fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
        if (fd < 0) {
                perror("Failed to open channel");
                return -1;
@@ -597,6 +888,7 @@ static int open_socket(uint16_t channel)
        if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                if (errno == EINVAL) {
                        /* Fallback to hcidump support */
+                       hcidump_fallback = true;
                        close(fd);
                        return -1;
                }
@@ -636,10 +928,207 @@ static int open_channel(uint16_t channel)
        return 0;
 }
 
+static void client_callback(int fd, uint32_t events, void *user_data)
+{
+       struct control_data *data = user_data;
+       ssize_t len;
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               mainloop_remove_fd(data->fd);
+               return;
+       }
+
+       len = recv(data->fd, data->buf + data->offset,
+                       sizeof(data->buf) - data->offset, MSG_DONTWAIT);
+       if (len < 0)
+               return;
+
+       data->offset += len;
+
+       if (data->offset > MGMT_HDR_SIZE) {
+               struct mgmt_hdr *hdr = (struct mgmt_hdr *) data->buf;
+               uint16_t pktlen = le16_to_cpu(hdr->len);
+
+               if (data->offset > pktlen + MGMT_HDR_SIZE) {
+                       uint16_t opcode = le16_to_cpu(hdr->opcode);
+                       uint16_t index = le16_to_cpu(hdr->index);
+
+                       packet_monitor(NULL, index, opcode,
+                                       data->buf + MGMT_HDR_SIZE, pktlen);
+
+                       data->offset -= pktlen + MGMT_HDR_SIZE;
+
+                       if (data->offset > 0)
+                               memmove(data->buf, data->buf +
+                                        MGMT_HDR_SIZE + pktlen, data->offset);
+               }
+       }
+}
+
+static void server_accept_callback(int fd, uint32_t events, void *user_data)
+{
+       struct control_data *data;
+       struct sockaddr_un addr;
+       socklen_t len;
+       int nfd;
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               mainloop_remove_fd(fd);
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       len = sizeof(addr);
+
+       nfd = accept(fd, (struct sockaddr *) &addr, &len);
+       if (nfd < 0) {
+               perror("Failed to accept client socket");
+               return;
+       }
+
+       printf("--- New monitor connection ---\n");
+
+       data = malloc(sizeof(*data));
+       if (!data) {
+               close(nfd);
+               return;
+       }
+
+       memset(data, 0, sizeof(*data));
+       data->channel = HCI_CHANNEL_MONITOR;
+       data->fd = nfd;
+
+        mainloop_add_fd(data->fd, EPOLLIN, client_callback, data, free_data);
+}
+
+static int server_fd = -1;
+
+void control_server(const char *path)
+{
+       struct sockaddr_un addr;
+       int fd;
+
+       if (server_fd >= 0)
+               return;
+
+       unlink(path);
+
+       fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (fd < 0) {
+               perror("Failed to open server socket");
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strcpy(addr.sun_path, path);
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to bind server socket");
+               close(fd);
+               return;
+       }
+
+       if (listen(fd, 5) < 0) {
+               perror("Failed to listen server socket");
+               close(fd);
+               return;
+       }
+
+       if (mainloop_add_fd(fd, EPOLLIN, server_accept_callback,
+                                               NULL, NULL) < 0) {
+               close(fd);
+               return;
+       }
+
+       server_fd = fd;
+}
+
+bool control_writer(const char *path)
+{
+       btsnoop_file = btsnoop_create(path, BTSNOOP_TYPE_MONITOR);
+
+       return !!btsnoop_file;
+}
+
+void control_reader(const char *path)
+{
+       unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
+       uint16_t pktlen;
+       uint32_t type;
+       struct timeval tv;
+
+       btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT);
+       if (!btsnoop_file)
+               return;
+
+       type = btsnoop_get_type(btsnoop_file);
+
+       switch (type) {
+       case BTSNOOP_TYPE_HCI:
+       case BTSNOOP_TYPE_UART:
+       case BTSNOOP_TYPE_SIMULATOR:
+               packet_del_filter(PACKET_FILTER_SHOW_INDEX);
+               break;
+
+       case BTSNOOP_TYPE_MONITOR:
+               packet_add_filter(PACKET_FILTER_SHOW_INDEX);
+               break;
+       }
+
+       open_pager();
+
+       switch (type) {
+       case BTSNOOP_TYPE_HCI:
+       case BTSNOOP_TYPE_UART:
+       case BTSNOOP_TYPE_MONITOR:
+               while (1) {
+                       uint16_t index, opcode;
+
+                       if (!btsnoop_read_hci(btsnoop_file, &tv, &index,
+                                                       &opcode, buf, &pktlen))
+                               break;
+
+                       if (opcode == 0xffff)
+                               continue;
+
+                       packet_monitor(&tv, index, opcode, buf, pktlen);
+                       ellisys_inject_hci(&tv, index, opcode, buf, pktlen);
+               }
+               break;
+
+       case BTSNOOP_TYPE_SIMULATOR:
+               while (1) {
+                       uint16_t frequency;
+
+                       if (!btsnoop_read_phy(btsnoop_file, &tv, &frequency,
+                                                               buf, &pktlen))
+                               break;
+
+                       packet_simulator(&tv, frequency, buf, pktlen);
+               }
+               break;
+       }
+
+       close_pager();
+
+       btsnoop_unref(btsnoop_file);
+}
+
 int control_tracing(void)
 {
-       if (open_channel(HCI_CHANNEL_MONITOR) < 0)
-               return -1;
+       packet_add_filter(PACKET_FILTER_SHOW_INDEX);
+
+       if (server_fd >= 0)
+               return 0;
+
+       if (open_channel(HCI_CHANNEL_MONITOR) < 0) {
+               if (!hcidump_fallback)
+                       return -1;
+               if (hcidump_tracing() < 0)
+                       return -1;
+               return 0;
+       }
 
        open_channel(HCI_CHANNEL_CONTROL);