blob: 2cc8762c0aeaffca0ce5811f9e6c53a79198db9c [file] [log] [blame]
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2011-2014 Intel Corporation
* Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
*
*
* 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 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
* Lesser General Public License for more details.
*
* 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
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include <time.h>
#include <sys/time.h>
#include <sys/socket.h>
#include "lib/bluetooth.h"
#include "lib/uuid.h"
#include "lib/hci.h"
#include "lib/hci_lib.h"
#include "src/shared/util.h"
#include "src/shared/btsnoop.h"
#include "display.h"
#include "bt.h"
#include "ll.h"
#include "hwdb.h"
#include "keys.h"
#include "l2cap.h"
#include "control.h"
#include "vendor.h"
#include "intel.h"
#include "broadcom.h"
#include "packet.h"
#define COLOR_CHANNEL_LABEL COLOR_WHITE
#define COLOR_FRAME_LABEL COLOR_WHITE
#define COLOR_INDEX_LABEL COLOR_WHITE
#define COLOR_TIMESTAMP COLOR_YELLOW
#define COLOR_NEW_INDEX COLOR_GREEN
#define COLOR_DEL_INDEX COLOR_RED
#define COLOR_OPEN_INDEX COLOR_GREEN
#define COLOR_CLOSE_INDEX COLOR_RED
#define COLOR_INDEX_INFO COLOR_GREEN
#define COLOR_VENDOR_DIAG COLOR_YELLOW
#define COLOR_SYSTEM_NOTE COLOR_OFF
#define COLOR_HCI_COMMAND COLOR_BLUE
#define COLOR_HCI_COMMAND_UNKNOWN COLOR_WHITE_BG
#define COLOR_HCI_EVENT COLOR_MAGENTA
#define COLOR_HCI_EVENT_UNKNOWN COLOR_WHITE_BG
#define COLOR_HCI_ACLDATA COLOR_CYAN
#define COLOR_HCI_SCODATA COLOR_YELLOW
#define COLOR_UNKNOWN_ERROR COLOR_WHITE_BG
#define COLOR_UNKNOWN_FEATURE_BIT COLOR_WHITE_BG
#define COLOR_UNKNOWN_COMMAND_BIT COLOR_WHITE_BG
#define COLOR_UNKNOWN_EVENT_MASK COLOR_WHITE_BG
#define COLOR_UNKNOWN_LE_STATES COLOR_WHITE_BG
#define COLOR_UNKNOWN_SERVICE_CLASS COLOR_WHITE_BG
#define COLOR_UNKNOWN_PKT_TYPE_BIT COLOR_WHITE_BG
#define COLOR_CTRL_OPEN COLOR_GREEN_BOLD
#define COLOR_CTRL_CLOSE COLOR_RED_BOLD
#define COLOR_CTRL_COMMAND COLOR_BLUE_BOLD
#define COLOR_CTRL_COMMAND_UNKNOWN COLOR_WHITE_BG
#define COLOR_CTRL_EVENT COLOR_MAGENTA_BOLD
#define COLOR_CTRL_EVENT_UNKNOWN COLOR_WHITE_BG
#define COLOR_UNKNOWN_OPTIONS_BIT COLOR_WHITE_BG
#define COLOR_UNKNOWN_SETTINGS_BIT COLOR_WHITE_BG
#define COLOR_UNKNOWN_ADDRESS_TYPE COLOR_WHITE_BG
#define COLOR_UNKNOWN_DEVICE_FLAG COLOR_WHITE_BG
#define COLOR_UNKNOWN_ADV_FLAG COLOR_WHITE_BG
#define COLOR_PHY_PACKET COLOR_BLUE
static time_t time_offset = ((time_t) -1);
static int priority_level = BTSNOOP_PRIORITY_INFO;
static unsigned long filter_mask = 0;
static bool index_filter = false;
static uint16_t index_number = 0;
static uint16_t index_current = 0;
#define UNKNOWN_MANUFACTURER 0xffff
#define CTRL_RAW 0x0000
#define CTRL_USER 0x0001
#define CTRL_MGMT 0x0002
#define MAX_CTRL 64
struct ctrl_data {
bool used;
uint32_t cookie;
uint16_t format;
char name[20];
};
static struct ctrl_data ctrl_list[MAX_CTRL];
static void assign_ctrl(uint32_t cookie, uint16_t format, const char *name)
{
int i;
for (i = 0; i < MAX_CTRL; i++) {
if (!ctrl_list[i].used) {
ctrl_list[i].used = true;
ctrl_list[i].cookie = cookie;
ctrl_list[i].format = format;
if (name) {
strncpy(ctrl_list[i].name, name, 19);
ctrl_list[i].name[19] = '\0';
} else
strcpy(ctrl_list[i].name, "null");
break;
}
}
}
static void release_ctrl(uint32_t cookie, uint16_t *format, char *name)
{
int i;
if (format)
*format = 0xffff;
for (i = 0; i < MAX_CTRL; i++) {
if (ctrl_list[i].used && ctrl_list[i].cookie == cookie) {
ctrl_list[i].used = false;
if (format)
*format = ctrl_list[i].format;
if (name)
strncpy(name, ctrl_list[i].name, 20);
break;
}
}
}
static uint16_t get_format(uint32_t cookie)
{
int i;
for (i = 0; i < MAX_CTRL; i++) {
if (ctrl_list[i].used && ctrl_list[i].cookie == cookie)
return ctrl_list[i].format;
}
return 0xffff;
}
#define MAX_CONN 16
struct conn_data {
uint16_t handle;
uint8_t type;
};
static struct conn_data conn_list[MAX_CONN];
static void assign_handle(uint16_t handle, uint8_t type)
{
int i;
for (i = 0; i < MAX_CONN; i++) {
if (conn_list[i].handle == 0x0000) {
conn_list[i].handle = handle;
conn_list[i].type = type;
break;
}
}
}
static void release_handle(uint16_t handle)
{
int i;
for (i = 0; i < MAX_CONN; i++) {
if (conn_list[i].handle == handle) {
conn_list[i].handle = 0x0000;
conn_list[i].type = 0x00;
break;
}
}
}
static uint8_t get_type(uint16_t handle)
{
int i;
for (i = 0; i < MAX_CONN; i++) {
if (conn_list[i].handle == handle)
return conn_list[i].type;
}
return 0xff;
}
bool packet_has_filter(unsigned long filter)
{
return filter_mask & filter;
}
void packet_set_filter(unsigned long filter)
{
filter_mask = filter;
}
void packet_add_filter(unsigned long filter)
{
if (index_filter)
filter &= ~PACKET_FILTER_SHOW_INDEX;
filter_mask |= filter;
}
void packet_del_filter(unsigned long filter)
{
filter_mask &= ~filter;
}
void packet_set_priority(const char *priority)
{
if (!priority)
return;
if (!strcasecmp(priority, "debug"))
priority_level = BTSNOOP_PRIORITY_DEBUG;
else
priority_level = atoi(priority);
}
void packet_select_index(uint16_t index)
{
filter_mask &= ~PACKET_FILTER_SHOW_INDEX;
index_filter = true;
index_number = index;
}
#define print_space(x) printf("%*c", (x), ' ');
#define MAX_INDEX 16
struct index_data {
uint8_t type;
uint8_t bdaddr[6];
uint16_t manufacturer;
size_t frame;
};
static struct index_data index_list[MAX_INDEX];
static void print_packet(struct timeval *tv, struct ucred *cred, char ident,
uint16_t index, const char *channel,
const char *color, const char *label,
const char *text, const char *extra)
{
int col = num_columns();
char line[256], ts_str[96];
int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
static size_t last_frame;
if (channel) {
if (use_color()) {
n = sprintf(ts_str + ts_pos, "%s", COLOR_CHANNEL_LABEL);
if (n > 0)
ts_pos += n;
}
n = sprintf(ts_str + ts_pos, " {%s}", channel);
if (n > 0) {
ts_pos += n;
ts_len += n;
}
} else if (index != HCI_DEV_NONE &&
index_list[index].frame != last_frame) {
if (use_color()) {
n = sprintf(ts_str + ts_pos, "%s", COLOR_FRAME_LABEL);
if (n > 0)
ts_pos += n;
}
n = sprintf(ts_str + ts_pos, " #%zu", index_list[index].frame);
if (n > 0) {
ts_pos += n;
ts_len += n;
}
last_frame = index_list[index].frame;
}
if ((filter_mask & PACKET_FILTER_SHOW_INDEX) &&
index != HCI_DEV_NONE) {
if (use_color()) {
n = sprintf(ts_str + ts_pos, "%s", COLOR_INDEX_LABEL);
if (n > 0)
ts_pos += n;
}
n = sprintf(ts_str + ts_pos, " [hci%d]", index);
if (n > 0) {
ts_pos += n;
ts_len += n;
}
}
if (tv) {
time_t t = tv->tv_sec;
struct tm tm;
localtime_r(&t, &tm);
if (use_color()) {
n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP);
if (n > 0)
ts_pos += n;
}
if (filter_mask & PACKET_FILTER_SHOW_DATE) {
n = sprintf(ts_str + ts_pos, " %04d-%02d-%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
if (n > 0) {
ts_pos += n;
ts_len += n;
}
}
if (filter_mask & PACKET_FILTER_SHOW_TIME) {
n = sprintf(ts_str + ts_pos, " %02d:%02d:%02d.%06lu",
tm.tm_hour, tm.tm_min, tm.tm_sec, tv->tv_usec);
if (n > 0) {
ts_pos += n;
ts_len += n;
}
}
if (filter_mask & PACKET_FILTER_SHOW_TIME_OFFSET) {
n = sprintf(ts_str + ts_pos, " %lu.%06lu",
tv->tv_sec - time_offset, tv->tv_usec);
if (n > 0) {
ts_pos += n;
ts_len += n;
}
}
}
if (use_color()) {
n = sprintf(ts_str + ts_pos, "%s", COLOR_OFF);
if (n > 0)
ts_pos += n;
}
if (use_color()) {
n = sprintf(line + pos, "%s", color);
if (n > 0)
pos += n;
}
n = sprintf(line + pos, "%c %s", ident, label ? label : "");
if (n > 0) {
pos += n;
len += n;
}
if (text) {
int extra_len = extra ? strlen(extra) : 0;
int max_len = col - len - extra_len - ts_len - 3;
n = snprintf(line + pos, max_len + 1, "%s%s",
label ? ": " : "", text);
if (n > max_len) {
line[pos + max_len - 1] = '.';
line[pos + max_len - 2] = '.';
if (line[pos + max_len - 3] == ' ')
line[pos + max_len - 3] = '.';
n = max_len;
}
if (n > 0) {
pos += n;
len += n;
}
}
if (use_color()) {
n = sprintf(line + pos, "%s", COLOR_OFF);
if (n > 0)
pos += n;
}
if (extra) {
n = sprintf(line + pos, " %s", extra);
if (n > 0) {
pos += n;
len += n;
}
}
if (ts_len > 0) {
printf("%s", line);
if (len < col)
print_space(col - len - ts_len - 1);
printf("%s%s\n", use_color() ? COLOR_TIMESTAMP : "", ts_str);
} else
printf("%s\n", line);
}
static const struct {
uint8_t error;
const char *str;
} error2str_table[] = {
{ 0x00, "Success" },
{ 0x01, "Unknown HCI Command" },
{ 0x02, "Unknown Connection Identifier" },
{ 0x03, "Hardware Failure" },
{ 0x04, "Page Timeout" },
{ 0x05, "Authentication Failure" },
{ 0x06, "PIN or Key Missing" },
{ 0x07, "Memory Capacity Exceeded" },
{ 0x08, "Connection Timeout" },
{ 0x09, "Connection Limit Exceeded" },
{ 0x0a, "Synchronous Connection Limit to a Device Exceeded" },
{ 0x0b, "ACL Connection Already Exists" },
{ 0x0c, "Command Disallowed" },
{ 0x0d, "Connection Rejected due to Limited Resources" },
{ 0x0e, "Connection Rejected due to Security Reasons" },
{ 0x0f, "Connection Rejected due to Unacceptable BD_ADDR" },
{ 0x10, "Connection Accept Timeout Exceeded" },
{ 0x11, "Unsupported Feature or Parameter Value" },
{ 0x12, "Invalid HCI Command Parameters" },
{ 0x13, "Remote User Terminated Connection" },
{ 0x14, "Remote Device Terminated due to Low Resources" },
{ 0x15, "Remote Device Terminated due to Power Off" },
{ 0x16, "Connection Terminated By Local Host" },
{ 0x17, "Repeated Attempts" },
{ 0x18, "Pairing Not Allowed" },
{ 0x19, "Unknown LMP PDU" },
{ 0x1a, "Unsupported Remote Feature / Unsupported LMP Feature" },
{ 0x1b, "SCO Offset Rejected" },
{ 0x1c, "SCO Interval Rejected" },
{ 0x1d, "SCO Air Mode Rejected" },
{ 0x1e, "Invalid LMP Parameters / Invalid LL Parameters" },
{ 0x1f, "Unspecified Error" },
{ 0x20, "Unsupported LMP Parameter Value / "
"Unsupported LL Parameter Value" },
{ 0x21, "Role Change Not Allowed" },
{ 0x22, "LMP Response Timeout / LL Response Timeout" },
{ 0x23, "LMP Error Transaction Collision" },
{ 0x24, "LMP PDU Not Allowed" },
{ 0x25, "Encryption Mode Not Acceptable" },
{ 0x26, "Link Key cannot be Changed" },
{ 0x27, "Requested QoS Not Supported" },
{ 0x28, "Instant Passed" },
{ 0x29, "Pairing With Unit Key Not Supported" },
{ 0x2a, "Different Transaction Collision" },
{ 0x2b, "Reserved" },
{ 0x2c, "QoS Unacceptable Parameter" },
{ 0x2d, "QoS Rejected" },
{ 0x2e, "Channel Classification Not Supported" },
{ 0x2f, "Insufficient Security" },
{ 0x30, "Parameter Out Of Manadatory Range" },
{ 0x31, "Reserved" },
{ 0x32, "Role Switch Pending" },
{ 0x33, "Reserved" },
{ 0x34, "Reserved Slot Violation" },
{ 0x35, "Role Switch Failed" },
{ 0x36, "Extended Inquiry Response Too Large" },
{ 0x37, "Secure Simple Pairing Not Supported By Host" },
{ 0x38, "Host Busy - Pairing" },
{ 0x39, "Connection Rejected due to No Suitable Channel Found" },
{ 0x3a, "Controller Busy" },
{ 0x3b, "Unacceptable Connection Parameters" },
{ 0x3c, "Advertising Timeout" },
{ 0x3d, "Connection Terminated due to MIC Failure" },
{ 0x3e, "Connection Failed to be Established" },
{ 0x3f, "MAC Connection Failed" },
{ 0x40, "Coarse Clock Adjustment Rejected "
"but Will Try to Adjust Using Clock Dragging" },
{ 0x41, "Type0 Submap Not Defined" },
{ 0x42, "Unknown Advertising Identifier" },
{ 0x43, "Limit Reached" },
{ 0x44, "Operation Cancelled by Host" },
{ }
};
static void print_error(const char *label, uint8_t error)
{
const char *str = "Unknown";
const char *color_on, *color_off;
bool unknown = true;
int i;
for (i = 0; error2str_table[i].str; i++) {
if (error2str_table[i].error == error) {
str = error2str_table[i].str;
unknown = false;
break;
}
}
if (use_color()) {
if (error) {
if (unknown)
color_on = COLOR_UNKNOWN_ERROR;
else
color_on = COLOR_RED;
} else
color_on = COLOR_GREEN;
color_off = COLOR_OFF;
} else {
color_on = "";
color_off = "";
}
print_field("%s: %s%s%s (0x%2.2x)", label,
color_on, str, color_off, error);
}
static void print_status(uint8_t status)
{
print_error("Status", status);
}
static void print_reason(uint8_t reason)
{
print_error("Reason", reason);
}
void packet_print_error(const char *label, uint8_t error)
{
print_error(label, error);
}
static void print_enable(const char *label, uint8_t enable)
{
const char *str;
switch (enable) {
case 0x00:
str = "Disabled";
break;
case 0x01:
str = "Enabled";
break;
default:
str = "Reserved";
break;
}
print_field("%s: %s (0x%2.2x)", label, str, enable);
}
static void print_addr_type(const char *label, uint8_t addr_type)
{
const char *str;
switch (addr_type) {
case 0x00:
str = "Public";
break;
case 0x01:
str = "Random";
break;
default:
str = "Reserved";
break;
}
print_field("%s: %s (0x%2.2x)", label, str, addr_type);
}
static void print_own_addr_type(uint8_t addr_type)
{
const char *str;
switch (addr_type) {
case 0x00:
case 0x02:
str = "Public";
break;
case 0x01:
case 0x03:
str = "Random";
break;
default:
str = "Reserved";
break;
}
print_field("Own address type: %s (0x%2.2x)", str, addr_type);
}
static void print_peer_addr_type(const char *label, uint8_t addr_type)
{
const char *str;
switch (addr_type) {
case 0x00:
str = "Public";
break;
case 0x01:
str = "Random";
break;
case 0x02:
str = "Resolved Public";
break;
case 0x03:
str = "Resolved Random";
break;
default:
str = "Reserved";
break;
}
print_field("%s: %s (0x%2.2x)", label, str, addr_type);
}
static void print_addr_resolve(const char *label, const uint8_t *addr,
uint8_t addr_type, bool resolve)
{
const char *str;
char *company;
switch (addr_type) {
case 0x00:
case 0x02:
if (!hwdb_get_company(addr, &company))
company = NULL;
if (company) {
print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
" (%s)", label, addr[5], addr[4],
addr[3], addr[2],
addr[1], addr[0],
company);
free(company);
} else {
print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
" (OUI %2.2X-%2.2X-%2.2X)", label,
addr[5], addr[4], addr[3],
addr[2], addr[1], addr[0],
addr[5], addr[4], addr[3]);
}
break;
case 0x01:
case 0x03:
switch ((addr[5] & 0xc0) >> 6) {
case 0x00:
str = "Non-Resolvable";
break;
case 0x01:
str = "Resolvable";
break;
case 0x03:
str = "Static";
break;
default:
str = "Reserved";
break;
}
print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X (%s)",
label, addr[5], addr[4], addr[3],
addr[2], addr[1], addr[0], str);
if (resolve && (addr[5] & 0xc0) == 0x40) {
uint8_t ident[6], ident_type;
if (keys_resolve_identity(addr, ident, &ident_type)) {
print_addr_type(" Identity type", ident_type);
print_addr_resolve(" Identity", ident,
ident_type, false);
}
}
break;
default:
print_field("%s: %2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2X",
label, addr[5], addr[4], addr[3],
addr[2], addr[1], addr[0]);
break;
}
}
static void print_addr(const char *label, const uint8_t *addr,
uint8_t addr_type)
{
print_addr_resolve(label, addr, addr_type, true);
}
static void print_bdaddr(const uint8_t *bdaddr)
{
print_addr("Address", bdaddr, 0x00);
}
static void print_lt_addr(uint8_t lt_addr)
{
print_field("LT address: %d", lt_addr);
}
static void print_handle(uint16_t handle)
{
print_field("Handle: %d", le16_to_cpu(handle));
}
static void print_phy_handle(uint8_t phy_handle)
{
print_field("Physical handle: %d", phy_handle);
}
static const struct {
uint8_t bit;
const char *str;
} pkt_type_table[] = {
{ 1, "2-DH1 may not be used" },
{ 2, "3-DH1 may not be used" },
{ 3, "DM1 may be used" },
{ 4, "DH1 may be used" },
{ 8, "2-DH3 may not be used" },
{ 9, "3-DH3 may not be used" },
{ 10, "DM3 may be used" },
{ 11, "DH3 may be used" },
{ 12, "3-DH5 may not be used" },
{ 13, "3-DH5 may not be used" },
{ 14, "DM5 may be used" },
{ 15, "DH5 may be used" },
{ }
};
static void print_pkt_type(uint16_t pkt_type)
{
uint16_t mask;
int i;
print_field("Packet type: 0x%4.4x", le16_to_cpu(pkt_type));
mask = le16_to_cpu(pkt_type);
for (i = 0; pkt_type_table[i].str; i++) {
if (le16_to_cpu(pkt_type) & (1 << pkt_type_table[i].bit)) {
print_field(" %s", pkt_type_table[i].str);
mask &= ~(1 << pkt_type_table[i].bit);
}
}
if (mask)
print_text(COLOR_UNKNOWN_PKT_TYPE_BIT,
" Unknown packet types (0x%4.4x)", mask);
}
static const struct {
uint8_t bit;
const char *str;
} pkt_type_sco_table[] = {
{ 0, "HV1 may be used" },
{ 1, "HV2 may be used" },
{ 2, "HV3 may be used" },
{ 3, "EV3 may be used" },
{ 4, "EV4 may be used" },
{ 5, "EV5 may be used" },
{ 6, "2-EV3 may not be used" },
{ 7, "3-EV3 may not be used" },
{ 8, "2-EV5 may not be used" },
{ 9, "3-EV5 may not be used" },
{ }
};
static void print_pkt_type_sco(uint16_t pkt_type)
{
uint16_t mask;
int i;
print_field("Packet type: 0x%4.4x", le16_to_cpu(pkt_type));
mask = le16_to_cpu(pkt_type);
for (i = 0; pkt_type_sco_table[i].str; i++) {
if (le16_to_cpu(pkt_type) & (1 << pkt_type_sco_table[i].bit)) {
print_field(" %s", pkt_type_sco_table[i].str);
mask &= ~(1 << pkt_type_sco_table[i].bit);
}
}
if (mask)
print_text(COLOR_UNKNOWN_PKT_TYPE_BIT,
" Unknown packet types (0x%4.4x)", mask);
}
static void print_iac(const uint8_t *lap)
{
const char *str = "";
if (lap[2] == 0x9e && lap[1] == 0x8b) {
switch (lap[0]) {
case 0x33:
str = " (General Inquiry)";
break;
case 0x00:
str = " (Limited Inquiry)";
break;
}
}
print_field("Access code: 0x%2.2x%2.2x%2.2x%s",
lap[2], lap[1], lap[0], str);
}
static void print_auth_enable(uint8_t enable)
{
const char *str;
switch (enable) {
case 0x00:
str = "Authentication not required";
break;
case 0x01:
str = "Authentication required for all connections";
break;
default:
str = "Reserved";
break;
}
print_field("Enable: %s (0x%2.2x)", str, enable);
}
static void print_encrypt_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "Encryption not required";
break;
case 0x01:
str = "Encryption required for all connections";
break;
default:
str = "Reserved";
break;
}
print_field("Mode: %s (0x%2.2x)", str, mode);
}
static const struct {
uint8_t bit;
const char *str;
} svc_class_table[] = {
{ 0, "Positioning (Location identification)" },
{ 1, "Networking (LAN, Ad hoc)" },
{ 2, "Rendering (Printing, Speaker)" },
{ 3, "Capturing (Scanner, Microphone)" },
{ 4, "Object Transfer (v-Inbox, v-Folder)" },
{ 5, "Audio (Speaker, Microphone, Headset)" },
{ 6, "Telephony (Cordless telephony, Modem, Headset)" },
{ 7, "Information (WEB-server, WAP-server)" },
{ }
};
static const struct {
uint8_t val;
const char *str;
} major_class_computer_table[] = {
{ 0x00, "Uncategorized, code for device not assigned" },
{ 0x01, "Desktop workstation" },
{ 0x02, "Server-class computer" },
{ 0x03, "Laptop" },
{ 0x04, "Handheld PC/PDA (clam shell)" },
{ 0x05, "Palm sized PC/PDA" },
{ 0x06, "Wearable computer (Watch sized)" },
{ 0x07, "Tablet" },
{ }
};
static const char *major_class_computer(uint8_t minor)
{
int i;
for (i = 0; major_class_computer_table[i].str; i++) {
if (major_class_computer_table[i].val == minor)
return major_class_computer_table[i].str;
}
return NULL;
}
static const struct {
uint8_t val;
const char *str;
} major_class_phone_table[] = {
{ 0x00, "Uncategorized, code for device not assigned" },
{ 0x01, "Cellular" },
{ 0x02, "Cordless" },
{ 0x03, "Smart phone" },
{ 0x04, "Wired modem or voice gateway" },
{ 0x05, "Common ISDN Access" },
{ }
};
static const char *major_class_phone(uint8_t minor)
{
int i;
for (i = 0; major_class_phone_table[i].str; i++) {
if (major_class_phone_table[i].val == minor)
return major_class_phone_table[i].str;
}
return NULL;
}
static const struct {
uint8_t val;
const char *str;
} major_class_av_table[] = {
{ 0x00, "Uncategorized, code for device not assigned" },
{ 0x01, "Wearable Headset Device" },
{ 0x02, "Hands-free Device" },
{ 0x04, "Microphone" },
{ 0x05, "Loudspeaker" },
{ 0x06, "Headphones" },
{ 0x07, "Portable Audio" },
{ 0x08, "Car audio" },
{ 0x09, "Set-top box" },
{ 0x0a, "HiFi Audio Device" },
{ 0x0b, "VCR" },
{ 0x0c, "Video Camera" },
{ 0x0d, "Camcorder" },
{ 0x0e, "Video Monitor" },
{ 0x0f, "Video Display and Loudspeaker" },
{ 0x10, "Video Conferencing" },
{ 0x12, "Gaming/Toy" },
{ }
};
static const char *major_class_av(uint8_t minor)
{
int i;
for (i = 0; major_class_av_table[i].str; i++) {
if (major_class_av_table[i].val == minor)
return major_class_av_table[i].str;
}
return NULL;
}
static const struct {
uint8_t val;
const char *str;
} major_class_wearable_table[] = {
{ 0x01, "Wrist Watch" },
{ 0x02, "Pager" },
{ 0x03, "Jacket" },
{ 0x04, "Helmet" },
{ 0x05, "Glasses" },
{ }
};
static const char *major_class_wearable(uint8_t minor)
{
int i;
for (i = 0; major_class_wearable_table[i].str; i++) {
if (major_class_wearable_table[i].val == minor)
return major_class_wearable_table[i].str;
}
return NULL;
}
static const struct {
uint8_t val;
const char *str;
const char *(*func)(uint8_t minor);
} major_class_table[] = {
{ 0x00, "Miscellaneous" },
{ 0x01, "Computer (desktop, notebook, PDA, organizers)",
major_class_computer },
{ 0x02, "Phone (cellular, cordless, payphone, modem)",
major_class_phone },
{ 0x03, "LAN /Network Access point" },
{ 0x04, "Audio/Video (headset, speaker, stereo, video, vcr)",
major_class_av },
{ 0x05, "Peripheral (mouse, joystick, keyboards)" },
{ 0x06, "Imaging (printing, scanner, camera, display)" },
{ 0x07, "Wearable", major_class_wearable },
{ 0x08, "Toy" },
{ 0x09, "Health" },
{ 0x1f, "Uncategorized, specific device code not specified" },
{ }
};
static void print_dev_class(const uint8_t *dev_class)
{
uint8_t mask, major_cls, minor_cls;
const char *major_str = NULL;
const char *minor_str = NULL;
int i;
print_field("Class: 0x%2.2x%2.2x%2.2x",
dev_class[2], dev_class[1], dev_class[0]);
if ((dev_class[0] & 0x03) != 0x00) {
print_field(" Format type: 0x%2.2x", dev_class[0] & 0x03);
print_text(COLOR_ERROR, " invalid format type");
return;
}
major_cls = dev_class[1] & 0x1f;
minor_cls = (dev_class[0] & 0xfc) >> 2;
for (i = 0; major_class_table[i].str; i++) {
if (major_class_table[i].val == major_cls) {
major_str = major_class_table[i].str;
if (!major_class_table[i].func)
break;
minor_str = major_class_table[i].func(minor_cls);
break;
}
}
if (major_str) {
print_field(" Major class: %s", major_str);
if (minor_str)
print_field(" Minor class: %s", minor_str);
else
print_field(" Minor class: 0x%2.2x", minor_cls);
} else {
print_field(" Major class: 0x%2.2x", major_cls);
print_field(" Minor class: 0x%2.2x", minor_cls);
}
if (dev_class[1] & 0x20)
print_field(" Limited Discoverable Mode");
if ((dev_class[1] & 0xc0) != 0x00) {
print_text(COLOR_ERROR, " invalid service class");
return;
}
mask = dev_class[2];
for (i = 0; svc_class_table[i].str; i++) {
if (dev_class[2] & (1 << svc_class_table[i].bit)) {
print_field(" %s", svc_class_table[i].str);
mask &= ~(1 << svc_class_table[i].bit);
}
}
if (mask)
print_text(COLOR_UNKNOWN_SERVICE_CLASS,
" Unknown service class (0x%2.2x)", mask);
}
static void print_appearance(uint16_t appearance)
{
print_field("Appearance: %s (0x%4.4x)", bt_appear_to_str(appearance),
appearance);
}
static void print_num_broadcast_retrans(uint8_t num_retrans)
{
print_field("Number of broadcast retransmissions: %u", num_retrans);
}
static void print_hold_mode_activity(uint8_t activity)
{
print_field("Activity: 0x%2.2x", activity);
if (activity == 0x00) {
print_field(" Maintain current Power State");
return;
}
if (activity & 0x01)
print_field(" Suspend Page Scan");
if (activity & 0x02)
print_field(" Suspend Inquiry Scan");
if (activity & 0x04)
print_field(" Suspend Periodic Inquiries");
}
static void print_power_type(uint8_t type)
{
const char *str;
switch (type) {
case 0x00:
str = "Current Transmit Power Level";
break;
case 0x01:
str = "Maximum Transmit Power Level";
break;
default:
str = "Reserved";
break;
}
print_field("Type: %s (0x%2.2x)", str, type);
}
static void print_power_level(int8_t level, const char *type)
{
print_field("TX power%s%s%s: %d dbm (0x%2.2x)",
type ? " (" : "", type ? type : "", type ? ")" : "",
level, level);
}
static void print_host_flow_control(uint8_t enable)
{
const char *str;
switch (enable) {
case 0x00:
str = "Off";
break;
case 0x01:
str = "ACL Data Packets";
break;
case 0x02:
str = "Synchronous Data Packets";
break;
case 0x03:
str = "ACL and Synchronous Data Packets";
break;
default:
str = "Reserved";
break;
}
print_field("Flow control: %s (0x%2.2x)", str, enable);
}
static void print_voice_setting(uint16_t setting)
{
uint8_t input_coding = (le16_to_cpu(setting) & 0x0300) >> 8;
uint8_t input_data_format = (le16_to_cpu(setting) & 0xc0) >> 6;
uint8_t air_coding_format = le16_to_cpu(setting) & 0x0003;
const char *str;
print_field("Setting: 0x%4.4x", le16_to_cpu(setting));
switch (input_coding) {
case 0x00:
str = "Linear";
break;
case 0x01:
str = "u-law";
break;
case 0x02:
str = "A-law";
break;
default:
str = "Reserved";
break;
}
print_field(" Input Coding: %s", str);
switch (input_data_format) {
case 0x00:
str = "1's complement";
break;
case 0x01:
str = "2's complement";
break;
case 0x02:
str = "Sign-Magnitude";
break;
case 0x03:
str = "Unsigned";
break;
default:
str = "Reserved";
break;
}
print_field(" Input Data Format: %s", str);
if (input_coding == 0x00) {
print_field(" Input Sample Size: %s",
le16_to_cpu(setting) & 0x20 ? "16-bit" : "8-bit");
print_field(" # of bits padding at MSB: %d",
(le16_to_cpu(setting) & 0x1c) >> 2);
}
switch (air_coding_format) {
case 0x00:
str = "CVSD";
break;
case 0x01:
str = "u-law";
break;
case 0x02:
str = "A-law";
break;
case 0x03:
str = "Transparent Data";
break;
default:
str = "Reserved";
break;
}
print_field(" Air Coding Format: %s", str);
}
static void print_retransmission_effort(uint8_t effort)
{
const char *str;
switch (effort) {
case 0x00:
str = "No retransmissions";
break;
case 0x01:
str = "Optimize for power consumption";
break;
case 0x02:
str = "Optimize for link quality";
break;
case 0xff:
str = "Don't care";
break;
default:
str = "Reserved";
break;
}
print_field("Retransmission effort: %s (0x%2.2x)", str, effort);
}
static void print_scan_enable(uint8_t scan_enable)
{
const char *str;
switch (scan_enable) {
case 0x00:
str = "No Scans";
break;
case 0x01:
str = "Inquiry Scan";
break;
case 0x02:
str = "Page Scan";
break;
case 0x03:
str = "Inquiry Scan + Page Scan";
break;
default:
str = "Reserved";
break;
}
print_field("Scan enable: %s (0x%2.2x)", str, scan_enable);
}
static void print_link_policy(uint16_t link_policy)
{
uint16_t policy = le16_to_cpu(link_policy);
print_field("Link policy: 0x%4.4x", policy);
if (policy == 0x0000) {
print_field(" Disable All Modes");
return;
}
if (policy & 0x0001)
print_field(" Enable Role Switch");
if (policy & 0x0002)
print_field(" Enable Hold Mode");
if (policy & 0x0004)
print_field(" Enable Sniff Mode");
if (policy & 0x0008)
print_field(" Enable Park State");
}
static void print_air_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "u-law log";
break;
case 0x01:
str = "A-law log";
break;
case 0x02:
str = "CVSD";
break;
case 0x03:
str = "Transparent";
break;
default:
str = "Reserved";
break;
}
print_field("Air mode: %s (0x%2.2x)", str, mode);
}
static void print_codec(const char *label, uint8_t codec)
{
const char *str;
switch (codec) {
case 0x00:
str = "u-law log";
break;
case 0x01:
str = "A-law log";
break;
case 0x02:
str = "CVSD";
break;
case 0x03:
str = "Transparent";
break;
case 0x04:
str = "Linear PCM";
break;
case 0x05:
str = "mSBC";
break;
case 0xff:
str = "Vendor specific";
break;
default:
str = "Reserved";
break;
}
print_field("%s: %s (0x%2.2x)", label, str, codec);
}
static void print_inquiry_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "Standard Inquiry Result";
break;
case 0x01:
str = "Inquiry Result with RSSI";
break;
case 0x02:
str = "Inquiry Result with RSSI or Extended Inquiry Result";
break;
default:
str = "Reserved";
break;
}
print_field("Mode: %s (0x%2.2x)", str, mode);
}
static void print_inquiry_scan_type(uint8_t type)
{
const char *str;
switch (type) {
case 0x00:
str = "Standard Scan";
break;
case 0x01:
str = "Interlaced Scan";
break;
default:
str = "Reserved";
break;
}
print_field("Type: %s (0x%2.2x)", str, type);
}
static void print_pscan_type(uint8_t type)
{
const char *str;
switch (type) {
case 0x00:
str = "Standard Scan";
break;
case 0x01:
str = "Interlaced Scan";
break;
default:
str = "Reserved";
break;
}
print_field("Type: %s (0x%2.2x)", str, type);
}
static void print_loopback_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "No Loopback";
break;
case 0x01:
str = "Local Loopback";
break;
case 0x02:
str = "Remote Loopback";
break;
default:
str = "Reserved";
break;
}
print_field("Mode: %s (0x%2.2x)", str, mode);
}
static void print_auth_payload_timeout(uint16_t timeout)
{
print_field("Timeout: %d msec (0x%4.4x)",
le16_to_cpu(timeout) * 10, le16_to_cpu(timeout));
}
static void print_pscan_rep_mode(uint8_t pscan_rep_mode)
{
const char *str;
switch (pscan_rep_mode) {
case 0x00:
str = "R0";
break;
case 0x01:
str = "R1";
break;
case 0x02:
str = "R2";
break;
default:
str = "Reserved";
break;
}
print_field("Page scan repetition mode: %s (0x%2.2x)",
str, pscan_rep_mode);
}
static void print_pscan_period_mode(uint8_t pscan_period_mode)
{
const char *str;
switch (pscan_period_mode) {
case 0x00:
str = "P0";
break;
case 0x01:
str = "P1";
break;
case 0x02:
str = "P2";
break;
default:
str = "Reserved";
break;
}
print_field("Page period mode: %s (0x%2.2x)", str, pscan_period_mode);
}
static void print_pscan_mode(uint8_t pscan_mode)
{
const char *str;
switch (pscan_mode) {
case 0x00:
str = "Mandatory";
break;
case 0x01:
str = "Optional I";
break;
case 0x02:
str = "Optional II";
break;
case 0x03:
str = "Optional III";
break;
default:
str = "Reserved";
break;
}
print_field("Page scan mode: %s (0x%2.2x)", str, pscan_mode);
}
static void print_clock_offset(uint16_t clock_offset)
{
print_field("Clock offset: 0x%4.4x", le16_to_cpu(clock_offset));
}
static void print_clock(uint32_t clock)
{
print_field("Clock: 0x%8.8x", le32_to_cpu(clock));
}
static void print_clock_type(uint8_t type)
{
const char *str;
switch (type) {
case 0x00:
str = "Local clock";
break;
case 0x01:
str = "Piconet clock";
break;
default:
str = "Reserved";
break;
}
print_field("Type: %s (0x%2.2x)", str, type);
}
static void print_clock_accuracy(uint16_t accuracy)
{
if (le16_to_cpu(accuracy) == 0xffff)
print_field("Accuracy: Unknown (0x%4.4x)",
le16_to_cpu(accuracy));
else
print_field("Accuracy: %.4f msec (0x%4.4x)",
le16_to_cpu(accuracy) * 0.3125,
le16_to_cpu(accuracy));
}
static void print_lpo_allowed(uint8_t lpo_allowed)
{
print_field("LPO allowed: 0x%2.2x", lpo_allowed);
}
static void print_broadcast_fragment(uint8_t fragment)
{
const char *str;
switch (fragment) {
case 0x00:
str = "Continuation fragment";
break;
case 0x01:
str = "Starting fragment";
break;
case 0x02:
str = "Ending fragment";
break;
case 0x03:
str = "No fragmentation";
break;
default:
str = "Reserved";
break;
}
print_field("Fragment: %s (0x%2.2x)", str, fragment);
}
static void print_link_type(uint8_t link_type)
{
const char *str;
switch (link_type) {
case 0x00:
str = "SCO";
break;
case 0x01:
str = "ACL";
break;
case 0x02:
str = "eSCO";
break;
default:
str = "Reserved";
break;
}
print_field("Link type: %s (0x%2.2x)", str, link_type);
}
static void print_encr_mode_change(uint8_t encr_mode, uint16_t handle)
{
const char *str;
uint8_t conn_type;
conn_type = get_type(le16_to_cpu(handle));
switch (encr_mode) {
case 0x00:
str = "Disabled";
break;
case 0x01:
switch (conn_type) {
case 0x00:
str = "Enabled with E0";
break;
case 0x01:
str = "Enabled with AES-CCM";
break;
default:
str = "Enabled";
break;
}
break;
case 0x02:
str = "Enabled with AES-CCM";
break;
default:
str = "Reserved";
break;
}
print_field("Encryption: %s (0x%2.2x)", str, encr_mode);
}
static void print_pin_type(uint8_t pin_type)
{
const char *str;
switch (pin_type) {
case 0x00:
str = "Variable";
break;
case 0x01:
str = "Fixed";
break;
default:
str = "Reserved";
break;
}
print_field("PIN type: %s (0x%2.2x)", str, pin_type);
}
static void print_key_flag(uint8_t key_flag)
{
const char *str;
switch (key_flag) {
case 0x00:
str = "Semi-permanent";
break;
case 0x01:
str = "Temporary";
break;
default:
str = "Reserved";
break;
}
print_field("Key flag: %s (0x%2.2x)", str, key_flag);
}
static void print_key_len(uint8_t key_len)
{
const char *str;
switch (key_len) {
case 32:
str = "802.11 PAL";
break;
default:
str = "Reserved";
break;
}
print_field("Key length: %s (%d)", str, key_len);
}
static void print_key_type(uint8_t key_type)
{
const char *str;
switch (key_type) {
case 0x00:
str = "Combination key";
break;
case 0x01:
str = "Local Unit key";
break;
case 0x02:
str = "Remote Unit key";
break;
case 0x03:
str = "Debug Combination key";
break;
case 0x04:
str = "Unauthenticated Combination key from P-192";
break;
case 0x05:
str = "Authenticated Combination key from P-192";
break;
case 0x06:
str = "Changed Combination key";
break;
case 0x07:
str = "Unauthenticated Combination key from P-256";
break;
case 0x08:
str = "Authenticated Combination key from P-256";
break;
default:
str = "Reserved";
break;
}
print_field("Key type: %s (0x%2.2x)", str, key_type);
}
static void print_key_size(uint8_t key_size)
{
print_field("Key size: %d", key_size);
}
static void print_hex_field(const char *label, const uint8_t *data,
uint8_t len)
{
char str[len * 2 + 1];
uint8_t i;
str[0] = '\0';
for (i = 0; i < len; i++)
sprintf(str + (i * 2), "%2.2x", data[i]);
print_field("%s: %s", label, str);
}
static void print_key(const char *label, const uint8_t *link_key)
{
print_hex_field(label, link_key, 16);
}
static void print_link_key(const uint8_t *link_key)
{
print_key("Link key", link_key);
}
static void print_pin_code(const uint8_t *pin_code, uint8_t pin_len)
{
char str[pin_len + 1];
uint8_t i;
for (i = 0; i < pin_len; i++)
sprintf(str + i, "%c", (const char) pin_code[i]);
print_field("PIN code: %s", str);
}
static void print_hash_p192(const uint8_t *hash)
{
print_key("Hash C from P-192", hash);
}
static void print_hash_p256(const uint8_t *hash)
{
print_key("Hash C from P-256", hash);
}
static void print_randomizer_p192(const uint8_t *randomizer)
{
print_key("Randomizer R with P-192", randomizer);
}
static void print_randomizer_p256(const uint8_t *randomizer)
{
print_key("Randomizer R with P-256", randomizer);
}
static void print_pk256(const char *label, const uint8_t *key)
{
print_field("%s:", label);
print_hex_field(" X", &key[0], 32);
print_hex_field(" Y", &key[32], 32);
}
static void print_dhkey(const uint8_t *dhkey)
{
print_hex_field("Diffie-Hellman key", dhkey, 32);
}
static void print_passkey(uint32_t passkey)
{
print_field("Passkey: %06d", le32_to_cpu(passkey));
}
static void print_io_capability(uint8_t capability)
{
const char *str;
switch (capability) {
case 0x00:
str = "DisplayOnly";
break;
case 0x01:
str = "DisplayYesNo";
break;
case 0x02:
str = "KeyboardOnly";
break;
case 0x03:
str = "NoInputNoOutput";
break;
default:
str = "Reserved";
break;
}
print_field("IO capability: %s (0x%2.2x)", str, capability);
}
static void print_oob_data(uint8_t oob_data)
{
const char *str;
switch (oob_data) {
case 0x00:
str = "Authentication data not present";
break;
case 0x01:
str = "P-192 authentication data present";
break;
case 0x02:
str = "P-256 authentication data present";
break;
case 0x03:
str = "P-192 and P-256 authentication data present";
break;
default:
str = "Reserved";
break;
}
print_field("OOB data: %s (0x%2.2x)", str, oob_data);
}
static void print_oob_data_response(uint8_t oob_data)
{
const char *str;
switch (oob_data) {
case 0x00:
str = "Authentication data not present";
break;
case 0x01:
str = "Authentication data present";
break;
default:
str = "Reserved";
break;
}
print_field("OOB data: %s (0x%2.2x)", str, oob_data);
}
static void print_authentication(uint8_t authentication)
{
const char *str;
switch (authentication) {
case 0x00:
str = "No Bonding - MITM not required";
break;
case 0x01:
str = "No Bonding - MITM required";
break;
case 0x02:
str = "Dedicated Bonding - MITM not required";
break;
case 0x03:
str = "Dedicated Bonding - MITM required";
break;
case 0x04:
str = "General Bonding - MITM not required";
break;
case 0x05:
str = "General Bonding - MITM required";
break;
default:
str = "Reserved";
break;
}
print_field("Authentication: %s (0x%2.2x)", str, authentication);
}
void packet_print_io_capability(uint8_t capability)
{
print_io_capability(capability);
}
void packet_print_io_authentication(uint8_t authentication)
{
print_authentication(authentication);
}
static void print_location_domain_aware(uint8_t aware)
{
const char *str;
switch (aware) {
case 0x00:
str = "Regulatory domain unknown";
break;
case 0x01:
str = "Regulatory domain known";
break;
default:
str = "Reserved";
break;
}
print_field("Domain aware: %s (0x%2.2x)", str, aware);
}
static void print_location_domain(const uint8_t *domain)
{
print_field("Domain: %c%c (0x%2.2x%2.2x)",
(char) domain[0], (char) domain[1], domain[0], domain[1]);
}
static void print_location_domain_options(uint8_t options)
{
print_field("Domain options: %c (0x%2.2x)", (char) options, options);
}
static void print_location_options(uint8_t options)
{
print_field("Options: 0x%2.2x", options);
}
static void print_flow_control_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "Packet based";
break;
case 0x01:
str = "Data block based";
break;
default:
str = "Reserved";
break;
}
print_field("Flow control mode: %s (0x%2.2x)", str, mode);
}
static void print_flow_direction(uint8_t direction)
{
const char *str;
switch (direction) {
case 0x00:
str = "Outgoing";
break;
case 0x01:
str = "Incoming";
break;
default:
str = "Reserved";
break;
}
print_field("Flow direction: %s (0x%2.2x)", str, direction);
}
static void print_service_type(uint8_t service_type)
{
const char *str;
switch (service_type) {
case 0x00:
str = "No Traffic";
break;
case 0x01:
str = "Best Effort";
break;
case 0x02:
str = "Guaranteed";
break;
default:
str = "Reserved";
break;
}
print_field("Service type: %s (0x%2.2x)", str, service_type);
}
static void print_flow_spec(const char *label, const uint8_t *data)
{
const char *str;
switch (data[1]) {
case 0x00:
str = "No traffic";
break;
case 0x01:
str = "Best effort";
break;
case 0x02:
str = "Guaranteed";
break;
default:
str = "Reserved";
break;
}
print_field("%s flow spec: 0x%2.2x", label, data[0]);
print_field(" Service type: %s (0x%2.2x)", str, data[1]);
print_field(" Maximum SDU size: 0x%4.4x", get_le16(data + 2));
print_field(" SDU inter-arrival time: 0x%8.8x", get_le32(data + 4));
print_field(" Access latency: 0x%8.8x", get_le32(data + 8));
print_field(" Flush timeout: 0x%8.8x", get_le32(data + 12));
}
static void print_amp_status(uint8_t amp_status)
{
const char *str;
switch (amp_status) {
case 0x00:
str = "Present";
break;
case 0x01:
str = "Bluetooth only";
break;
case 0x02:
str = "No capacity";
break;
case 0x03:
str = "Low capacity";
break;
case 0x04:
str = "Medium capacity";
break;
case 0x05:
str = "High capacity";
break;
case 0x06:
str = "Full capacity";
break;
default:
str = "Reserved";
break;
}
print_field("AMP status: %s (0x%2.2x)", str, amp_status);
}
static void print_num_resp(uint8_t num_resp)
{
print_field("Num responses: %d", num_resp);
}
static void print_num_reports(uint8_t num_reports)
{
print_field("Num reports: %d", num_reports);
}
static void print_adv_event_type(const char *label, uint8_t type)
{
const char *str;
switch (type) {
case 0x00:
str = "Connectable undirected - ADV_IND";
break;
case 0x01:
str = "Connectable directed - ADV_DIRECT_IND";
break;
case 0x02:
str = "Scannable undirected - ADV_SCAN_IND";
break;
case 0x03:
str = "Non connectable undirected - ADV_NONCONN_IND";
break;
case 0x04:
str = "Scan response - SCAN_RSP";
break;
default:
str = "Reserved";
break;
}
print_field("%s: %s (0x%2.2x)", label, str, type);
}
static void print_adv_channel_map(const char *label, uint8_t value)
{
const char *str;
switch (value) {
case 0x01:
str = "37";
break;
case 0x02:
str = "38";
break;
case 0x03:
str = "37, 38";
break;
case 0x04:
str = "39";
break;
case 0x05:
str = "37, 39";
break;
case 0x06:
str = "38, 39";
break;
case 0x07:
str = "37, 38, 39";
break;
default:
str = "Reserved";
break;
}
print_field("%s: %s (0x%2.2x)", label, str, value);
}
static void print_adv_filter_policy(const char *label, uint8_t value)
{
const char *str;
switch (value) {
case 0x00:
str = "Allow Scan Request from Any, "
"Allow Connect Request from Any";
break;
case 0x01:
str = "Allow Scan Request from White List Only, "
"Allow Connect Request from Any";
break;
case 0x02:
str = "Allow Scan Request from Any, "
"Allow Connect Request from White List Only";
break;
case 0x03:
str = "Allow Scan Request from White List Only, "
"Allow Connect Request from White List Only";
break;
default:
str = "Reserved";
break;
}
print_field("%s: %s (0x%2.2x)", label, str, value);
}
static void print_rssi(int8_t rssi)
{
if ((uint8_t) rssi == 0x99 || rssi == 127)
print_field("RSSI: invalid (0x%2.2x)", (uint8_t) rssi);
else
print_field("RSSI: %d dBm (0x%2.2x)", rssi, (uint8_t) rssi);
}
static void print_slot_625(const char *label, uint16_t value)
{
print_field("%s: %.3f msec (0x%4.4x)", label,
le16_to_cpu(value) * 0.625, le16_to_cpu(value));
}
static void print_slot_125(const char *label, uint16_t value)
{
print_field("%s: %.2f msec (0x%4.4x)", label,
le16_to_cpu(value) * 1.25, le16_to_cpu(value));
}
static void print_timeout(uint16_t timeout)
{
print_slot_625("Timeout", timeout);
}
static void print_interval(uint16_t interval)
{
print_slot_625("Interval", interval);
}
static void print_window(uint16_t window)
{
print_slot_625("Window", window);
}
static void print_conn_latency(const char *label, uint16_t value)
{
print_field("%s: %u (0x%4.4x)", label, le16_to_cpu(value),
le16_to_cpu(value));
}
static void print_role(uint8_t role)
{
const char *str;
switch (role) {
case 0x00:
str = "Master";
break;
case 0x01:
str = "Slave";
break;
default:
str = "Reserved";
break;
}
print_field("Role: %s (0x%2.2x)", str, role);
}
static void print_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "Active";
break;
case 0x01:
str = "Hold";
break;
case 0x02:
str = "Sniff";
break;
case 0x03:
str = "Park";
break;
default:
str = "Reserved";
break;
}
print_field("Mode: %s (0x%2.2x)", str, mode);
}
static void print_name(const uint8_t *name)
{
char str[249];
memcpy(str, name, 248);
str[248] = '\0';
print_field("Name: %s", str);
}
static void print_channel_map(const uint8_t *map)
{
unsigned int count = 0, start = 0;
char str[21];
int i, n;
for (i = 0; i < 10; i++)
sprintf(str + (i * 2), "%2.2x", map[i]);
print_field("Channel map: 0x%s", str);
for (i = 0; i < 10; i++) {
for (n = 0; n < 8; n++) {
if (map[i] & (1 << n)) {
if (count == 0)
start = (i * 8) + n;
count++;
continue;
}
if (count > 1) {
print_field(" Channel %u-%u",
start, start + count - 1);
count = 0;
} else if (count > 0) {
print_field(" Channel %u", start);
count = 0;
}
}
}
}
void packet_print_channel_map_lmp(const uint8_t *map)
{
print_channel_map(map);
}
static void print_flush_timeout(uint16_t timeout)
{
if (timeout)
print_timeout(timeout);
else
print_field("Timeout: No Automatic Flush");
}
void packet_print_version(const char *label, uint8_t version,
const char *sublabel, uint16_t subversion)
{
const char *str;
switch (version) {
case 0x00:
str = "Bluetooth 1.0b";
break;
case 0x01:
str = "Bluetooth 1.1";
break;
case 0x02:
str = "Bluetooth 1.2";
break;
case 0x03:
str = "Bluetooth 2.0";
break;
case 0x04:
str = "Bluetooth 2.1";
break;
case 0x05:
str = "Bluetooth 3.0";
break;
case 0x06:
str = "Bluetooth 4.0";
break;
case 0x07:
str = "Bluetooth 4.1";
break;
case 0x08:
str = "Bluetooth 4.2";
break;
case 0x09:
str = "Bluetooth 5.0";
break;
default:
str = "Reserved";
break;
}
if (sublabel)
print_field("%s: %s (0x%2.2x) - %s %d (0x%4.4x)",
label, str, version,
sublabel, subversion, subversion);
else
print_field("%s: %s (0x%2.2x)", label, str, version);
}
static void print_hci_version(uint8_t version, uint16_t revision)
{
packet_print_version("HCI version", version,
"Revision", le16_to_cpu(revision));
}
static void print_lmp_version(uint8_t version, uint16_t subversion)
{
packet_print_version("LMP version", version,
"Subversion", le16_to_cpu(subversion));
}
static void print_pal_version(uint8_t version, uint16_t subversion)
{
const char *str;
switch (version) {
case 0x01:
str = "Bluetooth 3.0";
break;
default:
str = "Reserved";
break;
}
print_field("PAL version: %s (0x%2.2x) - Subversion %d (0x%4.4x)",
str, version,
le16_to_cpu(subversion),
le16_to_cpu(subversion));
}
void packet_print_company(const char *label, uint16_t company)
{
print_field("%s: %s (%d)", label, bt_compidtostr(company), company);
}
static void print_manufacturer(uint16_t manufacturer)
{
packet_print_company("Manufacturer", le16_to_cpu(manufacturer));
}
static const struct {
uint16_t ver;
const char *str;
} broadcom_uart_subversion_table[] = {
{ 0x210b, "BCM43142A0" }, /* 001.001.011 */
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
{ }
};
static const struct {
uint16_t ver;
const char *str;
} broadcom_usb_subversion_table[] = {
{ 0x210b, "BCM43142A0" }, /* 001.001.011 */
{ 0x2112, "BCM4314A0" }, /* 001.001.018 */
{ 0x2118, "BCM20702A0" }, /* 001.001.024 */
{ 0x2126, "BCM4335A0" }, /* 001.001.038 */
{ 0x220e, "BCM20702A1" }, /* 001.002.014 */
{ 0x230f, "BCM4354A2" }, /* 001.003.015 */
{ 0x4106, "BCM4335B0" }, /* 002.001.006 */
{ 0x410e, "BCM20702B0" }, /* 002.001.014 */
{ 0x6109, "BCM4335C0" }, /* 003.001.009 */
{ 0x610c, "BCM4354" }, /* 003.001.012 */
{ }
};
static void print_manufacturer_broadcom(uint16_t subversion, uint16_t revision)
{
uint16_t ver = le16_to_cpu(subversion);
uint16_t rev = le16_to_cpu(revision);
const char *str = NULL;
int i;
switch ((rev & 0xf000) >> 12) {
case 0:
case 3:
for (i = 0; broadcom_uart_subversion_table[i].str; i++) {
if (broadcom_uart_subversion_table[i].ver == ver) {
str = broadcom_uart_subversion_table[i].str;
break;
}
}
break;
case 1:
case 2:
for (i = 0; broadcom_usb_subversion_table[i].str; i++) {
if (broadcom_usb_subversion_table[i].ver == ver) {
str = broadcom_usb_subversion_table[i].str;
break;
}
}
break;
}
if (str)
print_field(" Firmware: %3.3u.%3.3u.%3.3u (%s)",
(ver & 0xe000) >> 13,
(ver & 0x1f00) >> 8, ver & 0x00ff, str);
else
print_field(" Firmware: %3.3u.%3.3u.%3.3u",
(ver & 0xe000) >> 13,
(ver & 0x1f00) >> 8, ver & 0x00ff);
if (rev != 0xffff)
print_field(" Build: %4.4u", rev & 0x0fff);
}
static const char *get_supported_command(int bit);
static void print_commands(const uint8_t *commands)
{
unsigned int count = 0;
int i, n;
for (i = 0; i < 64; i++) {
for (n = 0; n < 8; n++) {
if (commands[i] & (1 << n))
count++;
}
}
print_field("Commands: %u entr%s", count, count == 1 ? "y" : "ies");
for (i = 0; i < 64; i++) {
for (n = 0; n < 8; n++) {
const char *cmd;
if (!(commands[i] & (1 << n)))
continue;
cmd = get_supported_command((i * 8) + n);
if (cmd)
print_field(" %s (Octet %d - Bit %d)",
cmd, i, n);
else
print_text(COLOR_UNKNOWN_COMMAND_BIT,
" Octet %d - Bit %d ", i, n);
}
}
}
struct features_data {
uint8_t bit;
const char *str;
};
static const struct features_data features_page0[] = {
{ 0, "3 slot packets" },
{ 1, "5 slot packets" },
{ 2, "Encryption" },
{ 3, "Slot offset" },
{ 4, "Timing accuracy" },
{ 5, "Role switch" },
{ 6, "Hold mode" },
{ 7, "Sniff mode" },
{ 8, "Park state" },
{ 9, "Power control requests" },
{ 10, "Channel quality driven data rate (CQDDR)"},
{ 11, "SCO link" },
{ 12, "HV2 packets" },
{ 13, "HV3 packets" },
{ 14, "u-law log synchronous data" },
{ 15, "A-law log synchronous data" },
{ 16, "CVSD synchronous data" },
{ 17, "Paging parameter negotiation" },
{ 18, "Power control" },
{ 19, "Transparent synchronous data" },
{ 20, "Flow control lag (least significant bit)"},
{ 21, "Flow control lag (middle bit)" },
{ 22, "Flow control lag (most significant bit)" },
{ 23, "Broadcast Encryption" },
{ 25, "Enhanced Data Rate ACL 2 Mbps mode" },
{ 26, "Enhanced Data Rate ACL 3 Mbps mode" },
{ 27, "Enhanced inquiry scan" },
{ 28, "Interlaced inquiry scan" },
{ 29, "Interlaced page scan" },
{ 30, "RSSI with inquiry results" },
{ 31, "Extended SCO link (EV3 packets)" },
{ 32, "EV4 packets" },
{ 33, "EV5 packets" },
{ 35, "AFH capable slave" },
{ 36, "AFH classification slave" },
{ 37, "BR/EDR Not Supported" },
{ 38, "LE Supported (Controller)" },
{ 39, "3-slot Enhanced Data Rate ACL packets" },
{ 40, "5-slot Enhanced Data Rate ACL packets" },
{ 41, "Sniff subrating" },
{ 42, "Pause encryption" },
{ 43, "AFH capable master" },
{ 44, "AFH classification master" },
{ 45, "Enhanced Data Rate eSCO 2 Mbps mode" },
{ 46, "Enhanced Data Rate eSCO 3 Mbps mode" },
{ 47, "3-slot Enhanced Data Rate eSCO packets" },
{ 48, "Extended Inquiry Response" },
{ 49, "Simultaneous LE and BR/EDR (Controller)" },
{ 51, "Secure Simple Pairing" },
{ 52, "Encapsulated PDU" },
{ 53, "Erroneous Data Reporting" },
{ 54, "Non-flushable Packet Boundary Flag" },
{ 56, "Link Supervision Timeout Changed Event" },
{ 57, "Inquiry TX Power Level" },
{ 58, "Enhanced Power Control" },
{ 63, "Extended features" },
{ }
};
static const struct features_data features_page1[] = {
{ 0, "Secure Simple Pairing (Host Support)" },
{ 1, "LE Supported (Host)" },
{ 2, "Simultaneous LE and BR/EDR (Host)" },
{ 3, "Secure Connections (Host Support)" },
{ }
};
static const struct features_data features_page2[] = {
{ 0, "Connectionless Slave Broadcast - Master" },
{ 1, "Connectionless Slave Broadcast - Slave" },
{ 2, "Synchronization Train" },
{ 3, "Synchronization Scan" },
{ 4, "Inquiry Response Notification Event" },
{ 5, "Generalized interlaced scan" },
{ 6, "Coarse Clock Adjustment" },
{ 8, "Secure Connections (Controller Support)" },
{ 9, "Ping" },
{ 10, "Slot Availability Mask" },
{ 11, "Train nudging" },
{ }
};
static const struct features_data features_le[] = {
{ 0, "LE Encryption" },
{ 1, "Connection Parameter Request Procedure" },
{ 2, "Extended Reject Indication" },
{ 3, "Slave-initiated Features Exchange" },
{ 4, "LE Ping" },
{ 5, "LE Data Packet Length Extension" },
{ 6, "LL Privacy" },
{ 7, "Extended Scanner Filter Policies" },
{ 8, "LE 2M PHY" },
{ 9, "Stable Modulation Index - Transmitter" },
{ 10, "Stable Modulation Index - Receiver" },
{ 11, "LE Coded PHY" },
{ 12, "LE Extended Advertising" },
{ 13, "LE Periodic Advertising" },
{ 14, "Channel Selection Algorithm #2" },
{ 15, "LE Power Class 1" },
{ 16, "Minimum Number of Used Channels Procedure"},
{ }
};
static void print_features(uint8_t page, const uint8_t *features_array,
uint8_t type)
{
const struct features_data *features_table = NULL;
uint64_t mask, features = 0;
char str[41];
int i;
for (i = 0; i < 8; i++) {
sprintf(str + (i * 5), " 0x%2.2x", features_array[i]);
features |= ((uint64_t) features_array[i]) << (i * 8);
}
print_field("Features:%s", str);
switch (type) {
case 0x00:
switch (page) {
case 0:
features_table = features_page0;
break;
case 1:
features_table = features_page1;
break;
case 2:
features_table = features_page2;
break;
}
break;
case 0x01:
switch (page) {
case 0:
features_table = features_le;
break;
}
break;
}
if (!features_table)
return;
mask = features;
for (i = 0; features_table[i].str; i++) {
if (features & (((uint64_t) 1) << features_table[i].bit)) {
print_field(" %s", features_table[i].str);
mask &= ~(((uint64_t) 1) << features_table[i].bit);
}
}
if (mask)
print_text(COLOR_UNKNOWN_FEATURE_BIT, " Unknown features "
"(0x%16.16" PRIx64 ")", mask);
}
void packet_print_features_lmp(const uint8_t *features, uint8_t page)
{
print_features(page, features, 0x00);
}
void packet_print_features_ll(const uint8_t *features)
{
print_features(0, features, 0x01);
}
#define LE_STATE_SCAN_ADV 0x0001
#define LE_STATE_CONN_ADV 0x0002
#define LE_STATE_NONCONN_ADV 0x0004
#define LE_STATE_HIGH_DIRECT_ADV 0x0008
#define LE_STATE_LOW_DIRECT_ADV 0x0010
#define LE_STATE_ACTIVE_SCAN 0x0020
#define LE_STATE_PASSIVE_SCAN 0x0040
#define LE_STATE_INITIATING 0x0080
#define LE_STATE_CONN_MASTER 0x0100
#define LE_STATE_CONN_SLAVE 0x0200
#define LE_STATE_MASTER_MASTER 0x0400
#define LE_STATE_SLAVE_SLAVE 0x0800
#define LE_STATE_MASTER_SLAVE 0x1000
static const struct {
uint8_t bit;
const char *str;
} le_states_desc_table[] = {
{ 0, "Scannable Advertising State" },
{ 1, "Connectable Advertising State" },
{ 2, "Non-connectable Advertising State" },
{ 3, "High Duty Cycle Directed Advertising State" },
{ 4, "Low Duty Cycle Directed Advertising State" },
{ 5, "Active Scanning State" },
{ 6, "Passive Scanning State" },
{ 7, "Initiating State" },
{ 8, "Connection State (Master Role)" },
{ 9, "Connection State (Slave Role)" },
{ 10, "Master Role & Master Role" },
{ 11, "Slave Role & Slave Role" },
{ 12, "Master Role & Slave Role" },
{ }
};
static const struct {
uint8_t bit;
uint16_t states;
} le_states_comb_table[] = {
{ 0, LE_STATE_NONCONN_ADV },
{ 1, LE_STATE_SCAN_ADV },
{ 2, LE_STATE_CONN_ADV },
{ 3, LE_STATE_HIGH_DIRECT_ADV },
{ 4, LE_STATE_PASSIVE_SCAN },
{ 5, LE_STATE_ACTIVE_SCAN },
{ 6, LE_STATE_INITIATING | LE_STATE_CONN_MASTER },
{ 7, LE_STATE_CONN_SLAVE },
{ 8, LE_STATE_PASSIVE_SCAN | LE_STATE_NONCONN_ADV },
{ 9, LE_STATE_PASSIVE_SCAN | LE_STATE_SCAN_ADV },
{ 10, LE_STATE_PASSIVE_SCAN | LE_STATE_CONN_ADV },
{ 11, LE_STATE_PASSIVE_SCAN | LE_STATE_HIGH_DIRECT_ADV },
{ 12, LE_STATE_ACTIVE_SCAN | LE_STATE_NONCONN_ADV },
{ 13, LE_STATE_ACTIVE_SCAN | LE_STATE_SCAN_ADV },
{ 14, LE_STATE_ACTIVE_SCAN | LE_STATE_CONN_ADV },
{ 15, LE_STATE_ACTIVE_SCAN | LE_STATE_HIGH_DIRECT_ADV },
{ 16, LE_STATE_INITIATING | LE_STATE_NONCONN_ADV },
{ 17, LE_STATE_INITIATING | LE_STATE_SCAN_ADV },
{ 18, LE_STATE_CONN_MASTER | LE_STATE_NONCONN_ADV },
{ 19, LE_STATE_CONN_MASTER | LE_STATE_SCAN_ADV },
{ 20, LE_STATE_CONN_SLAVE | LE_STATE_NONCONN_ADV },
{ 21, LE_STATE_CONN_SLAVE | LE_STATE_SCAN_ADV },
{ 22, LE_STATE_INITIATING | LE_STATE_PASSIVE_SCAN },
{ 23, LE_STATE_INITIATING | LE_STATE_ACTIVE_SCAN },
{ 24, LE_STATE_CONN_MASTER | LE_STATE_PASSIVE_SCAN },
{ 25, LE_STATE_CONN_MASTER | LE_STATE_ACTIVE_SCAN },
{ 26, LE_STATE_CONN_SLAVE | LE_STATE_PASSIVE_SCAN },
{ 27, LE_STATE_CONN_SLAVE | LE_STATE_ACTIVE_SCAN },
{ 28, LE_STATE_INITIATING | LE_STATE_CONN_MASTER |
LE_STATE_MASTER_MASTER },
{ 29, LE_STATE_LOW_DIRECT_ADV },
{ 30, LE_STATE_LOW_DIRECT_ADV | LE_STATE_PASSIVE_SCAN },
{ 31, LE_STATE_LOW_DIRECT_ADV | LE_STATE_ACTIVE_SCAN },
{ 32, LE_STATE_INITIATING | LE_STATE_CONN_ADV |
LE_STATE_MASTER_SLAVE },
{ 33, LE_STATE_INITIATING | LE_STATE_HIGH_DIRECT_ADV |
LE_STATE_MASTER_SLAVE },
{ 34, LE_STATE_INITIATING | LE_STATE_LOW_DIRECT_ADV |
LE_STATE_MASTER_SLAVE },
{ 35, LE_STATE_CONN_MASTER | LE_STATE_CONN_ADV |
LE_STATE_MASTER_SLAVE },
{ 36, LE_STATE_CONN_MASTER | LE_STATE_HIGH_DIRECT_ADV |
LE_STATE_MASTER_SLAVE },
{ 37, LE_STATE_CONN_MASTER | LE_STATE_LOW_DIRECT_ADV |
LE_STATE_MASTER_SLAVE },
{ 38, LE_STATE_CONN_SLAVE | LE_STATE_CONN_ADV |
LE_STATE_MASTER_SLAVE },
{ 39, LE_STATE_CONN_SLAVE | LE_STATE_HIGH_DIRECT_ADV |
LE_STATE_SLAVE_SLAVE },
{ 40, LE_STATE_CONN_SLAVE | LE_STATE_LOW_DIRECT_ADV |
LE_STATE_SLAVE_SLAVE },
{ 41, LE_STATE_INITIATING | LE_STATE_CONN_SLAVE |
LE_STATE_MASTER_SLAVE },
{ }
};
static void print_le_states(const uint8_t *states_array)
{
uint64_t mask, states = 0;
int i, n;
for (i = 0; i < 8; i++)
states |= ((uint64_t) states_array[i]) << (i * 8);
print_field("States: 0x%16.16" PRIx64, states);
mask = states;
for (i = 0; le_states_comb_table[i].states; i++) {
uint64_t val = (((uint64_t) 1) << le_states_comb_table[i].bit);
const char *str[3] = { NULL, };
int num = 0;
if (!(states & val))
continue;
for (n = 0; n < 16; n++) {
if (le_states_comb_table[i].states & (1 << n))
str[num++] = le_states_desc_table[n].str;
}
if (num > 0) {
print_field(" %s", str[0]);
for (n = 1; n < num; n++)
print_field(" and %s", str[n]);
}
mask &= ~val;
}
if (mask)
print_text(COLOR_UNKNOWN_LE_STATES, " Unknown states "
"(0x%16.16" PRIx64 ")", mask);
}
static void print_le_channel_map(const uint8_t *map)
{
unsigned int count = 0, start = 0;
char str[11];
int i, n;
for (i = 0; i < 5; i++)
sprintf(str + (i * 2), "%2.2x", map[i]);
print_field("Channel map: 0x%s", str);
for (i = 0; i < 5; i++) {
for (n = 0; n < 8; n++) {
if (map[i] & (1 << n)) {
if (count == 0)
start = (i * 8) + n;
count++;
continue;
}
if (count > 1) {
print_field(" Channel %u-%u",
start, start + count - 1);
count = 0;
} else if (count > 0) {
print_field(" Channel %u", start);
count = 0;
}
}
}
}
void packet_print_channel_map_ll(const uint8_t *map)
{
print_le_channel_map(map);
}
static void print_random_number(uint64_t rand)
{
print_field("Random number: 0x%16.16" PRIx64, le64_to_cpu(rand));
}
static void print_encrypted_diversifier(uint16_t ediv)
{
print_field("Encrypted diversifier: 0x%4.4x", le16_to_cpu(ediv));
}
static const struct {
uint8_t bit;
const char *str;
} events_table[] = {
{ 0, "Inquiry Complete" },
{ 1, "Inquiry Result" },
{ 2, "Connection Complete" },
{ 3, "Connection Request" },
{ 4, "Disconnection Complete" },
{ 5, "Authentication Complete" },
{ 6, "Remote Name Request Complete" },
{ 7, "Encryption Change" },
{ 8, "Change Connection Link Key Complete" },
{ 9, "Master Link Key Complete" },
{ 10, "Read Remote Supported Features Complete" },
{ 11, "Read Remote Version Information Complete" },
{ 12, "QoS Setup Complete" },
{ 13, "Command Complete" },
{ 14, "Command Status" },
{ 15, "Hardware Error" },
{ 16, "Flush Occurred" },
{ 17, "Role Change" },
{ 18, "Number of Completed Packets" },
{ 19, "Mode Change" },
{ 20, "Return Link Keys" },
{ 21, "PIN Code Request" },
{ 22, "Link Key Request" },
{ 23, "Link Key Notification" },
{ 24, "Loopback Command" },
{ 25, "Data Buffer Overflow" },
{ 26, "Max Slots Change" },
{ 27, "Read Clock Offset Complete" },
{ 28, "Connection Packet Type Changed" },
{ 29, "QoS Violation" },
{ 30, "Page Scan Mode Change" },
{ 31, "Page Scan Repetition Mode Change" },
{ 32, "Flow Specification Complete" },
{ 33, "Inquiry Result with RSSI" },
{ 34, "Read Remote Extended Features Complete" },
{ 43, "Synchronous Connection Complete" },
{ 44, "Synchronous Connection Changed" },
{ 45, "Sniff Subrating" },
{ 46, "Extended Inquiry Result" },
{ 47, "Encryption Key Refresh Complete" },
{ 48, "IO Capability Request" },
{ 49, "IO Capability Request Reply" },
{ 50, "User Confirmation Request" },
{ 51, "User Passkey Request" },
{ 52, "Remote OOB Data Request" },
{ 53, "Simple Pairing Complete" },
{ 55, "Link Supervision Timeout Changed" },
{ 56, "Enhanced Flush Complete" },
{ 58, "User Passkey Notification" },
{ 59, "Keypress Notification" },
{ 60, "Remote Host Supported Features Notification" },
{ 61, "LE Meta" },
{ }
};
static void print_event_mask(const uint8_t *events_array)
{
uint64_t mask, events = 0;
int i;
for (i = 0; i < 8; i++)
events |= ((uint64_t) events_array[i]) << (i * 8);
print_field("Mask: 0x%16.16" PRIx64, events);
mask = events;
for (i = 0; events_table[i].str; i++) {
if (events & (((uint64_t) 1) << events_table[i].bit)) {
print_field(" %s", events_table[i].str);
mask &= ~(((uint64_t) 1) << events_table[i].bit);
}
}
if (mask)
print_text(COLOR_UNKNOWN_EVENT_MASK, " Unknown mask "
"(0x%16.16" PRIx64 ")", mask);
}
static const struct {
uint8_t bit;
const char *str;
} events_page2_table[] = {
{ 0, "Physical Link Complete" },
{ 1, "Channel Selected" },
{ 2, "Disconnection Physical Link Complete" },
{ 3, "Physical Link Loss Early Warning" },
{ 4, "Physical Link Recovery" },
{ 5, "Logical Link Complete" },
{ 6, "Disconnection Logical Link Complete" },
{ 7, "Flow Specification Modify Complete" },
{ 8, "Number of Completed Data Blocks" },
{ 9, "AMP Start Test" },
{ 10, "AMP Test End" },
{ 11, "AMP Receiver Report" },
{ 12, "Short Range Mode Change Complete" },
{ 13, "AMP Status Change" },
{ 14, "Triggered Clock Capture" },
{ 15, "Synchronization Train Complete" },
{ 16, "Synchronization Train Received" },
{ 17, "Connectionless Slave Broadcast Receive" },
{ 18, "Connectionless Slave Broadcast Timeout" },
{ 19, "Truncated Page Complete" },
{ 20, "Slave Page Response Timeout" },
{ 21, "Connectionless Slave Broadcast Channel Map Change" },
{ 22, "Inquiry Response Notification" },
{ 23, "Authenticated Payload Timeout Expired" },
{ 24, "SAM Status Change" },
{ }
};
static void print_event_mask_page2(const uint8_t *events_array)
{
uint64_t mask, events = 0;
int i;
for (i = 0; i < 8; i++)
events |= ((uint64_t) events_array[i]) << (i * 8);
print_field("Mask: 0x%16.16" PRIx64, events);
mask = events;
for (i = 0; events_page2_table[i].str; i++) {
if (events & (((uint64_t) 1) << events_page2_table[i].bit)) {
print_field(" %s", events_page2_table[i].str);
mask &= ~(((uint64_t) 1) << events_page2_table[i].bit);
}
}
if (mask)
print_text(COLOR_UNKNOWN_EVENT_MASK, " Unknown mask "
"(0x%16.16" PRIx64 ")", mask);
}
static const struct {
uint8_t bit;
const char *str;
} events_le_table[] = {
{ 0, "LE Connection Complete" },
{ 1, "LE Advertising Report" },
{ 2, "LE Connection Update Complete" },
{ 3, "LE Read Remote Used Features Complete" },
{ 4, "LE Long Term Key Request" },
{ 5, "LE Remote Connection Parameter Request" },
{ 6, "LE Data Length Change" },
{ 7, "LE Read Local P-256 Public Key Complete" },
{ 8, "LE Generate DHKey Complete" },
{ 9, "LE Enhanced Connection Complete" },
{ 10, "LE Direct Advertising Report" },
{ 11, "LE PHY Update Complete" },
{ 12, "LE Extended Advertising Report" },
{ 13, "LE Periodic Advertising Sync Established"},
{ 14, "LE Periodic Advertising Report" },
{ 15, "LE Periodic Advertising Sync Lost" },
{ 16, "LE Extended Scan Timeout" },
{ 17, "LE Extended Advertising Set Terminated" },
{ 18, "LE Scan Request Received" },
{ 19, "LE Channel Selection Algorithm" },
{ }
};
static void print_event_mask_le(const uint8_t