blob: 6272562d19afad946378de2cc343d8bfbd72f1ef [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/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 "uuid.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_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;
}
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), ' ');
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;
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;
}
}
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, "Directed 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" },
{ }
};
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_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 const struct {
uint16_t val;
bool generic;
const char *str;
} appearance_table[] = {
{ 0, true, "Unknown" },
{ 64, true, "Phone" },
{ 128, true, "Computer" },
{ 192, true, "Watch" },
{ 193, false, "Sports Watch" },
{ 256, true, "Clock" },
{ 320, true, "Display" },
{ 384, true, "Remote Control" },
{ 448, true, "Eye-glasses" },
{ 512, true, "Tag" },
{ 576, true, "Keyring" },
{ 640, true, "Media Player" },
{ 704, true, "Barcode Scanner" },
{ 768, true, "Thermometer" },
{ 769, false, "Thermometer: Ear" },
{ 832, true, "Heart Rate Sensor" },
{ 833, false, "Heart Rate Belt" },
{ 896, true, "Blood Pressure" },
{ 897, false, "Blood Pressure: Arm" },
{ 898, false, "Blood Pressure: Wrist" },
{ 960, true, "Human Interface Device" },
{ 961, false, "Keyboard" },
{ 962, false, "Mouse" },
{ 963, false, "Joystick" },
{ 964, false, "Gamepad" },
{ 965, false, "Digitizer Tablet" },
{ 966, false, "Card Reader" },
{ 967, false, "Digital Pen" },
{ 968, false, "Barcode Scanner" },
{ 1024, true, "Glucose Meter" },
{ 1088, true, "Running Walking Sensor" },
{ 1089, false, "Running Walking Sensor: In-Shoe" },
{ 1090, false, "Running Walking Sensor: On-Shoe" },
{ 1091, false, "Running Walking Sensor: On-Hip" },
{ 1152, true, "Cycling" },
{ 1153, false, "Cycling: Cycling Computer" },
{ 1154, false, "Cycling: Speed Sensor" },
{ 1155, false, "Cycling: Cadence Sensor" },
{ 1156, false, "Cycling: Power Sensor" },
{ 1157, false, "Cycling: Speed and Cadence Sensor" },
{ 1216, true, "Undefined" },
{ 3136, true, "Pulse Oximeter" },
{ 3137, false, "Pulse Oximeter: Fingertip" },
{ 3138, false, "Pulse Oximeter: Wrist Worn" },
{ 3200, true, "Weight Scale" },
{ 3264, true, "Undefined" },
{ 5184, true, "Outdoor Sports Activity" },
{ 5185, false, "Location Display Device" },
{ 5186, false, "Location and Navigation Display Device" },
{ 5187, false, "Location Pod" },
{ 5188, false, "Location and Navigation Pod" },
{ 5248, true, "Undefined" },
{ }
};
static void print_appearance(uint16_t appearance)
{
const char *str = NULL;
int i, type = 0;
for (i = 0; appearance_table[i].str; i++) {
if (appearance_table[i].generic) {
if (appearance < appearance_table[i].val)
break;
type = i;
}
if (appearance_table[i].val == appearance) {
str = appearance_table[i].str;
break;
}
}
if (!str)
str = appearance_table[type].str;
print_field("Appearance: %s (0x%4.4x)", str, 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",
type ? " (" : "", type ? type : "", type ? ")" : "", level);
}
static void print_sync_flow_control(uint8_t enable)
{
const char *str;
switch (enable) {
case 0x00:
str = "Disabled";
break;
case 0x01:
str = "Enabled";
break;
default:
str = "Reserved";
break;
}
print_field("Flow control: %s (0x%2.2x)", str, enable);
}
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_afh_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "Disabled";
break;
case 0x01:
str = "Enabled";
break;
default:
str = "Reserved";
break;
}
print_field("Mode: %s (0x%2.2x)", str, mode);
}
static void print_erroneous_reporting(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "Disabled";
break;
case 0x01:
str = "Enabled";
break;
default:
str = "Reserved";
break;
}
print_field("Mode: %s (0x%2.2x)", str, mode);
}
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_simple_pairing_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "Disabled";
break;
case 0x01:
str = "Enabled";
break;
default:
str = "Reserved";
break;
}
print_field("Mode: %s (0x%2.2x)", str, mode);
}
static void print_ssp_debug_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "Disabled";
break;
case 0x01:
str = "Enabled";
break;
default:
str = "Reserved";
break;
}
print_field("Debug mode: %s (0x%2.2x)", str, mode);
}
static void print_secure_conn_support(uint8_t support)
{
const char *str;
switch (support) {
case 0x00:
str = "Disabled";
break;
case 0x01:
str = "Enabled";
break;
default:
str = "Reserved";
break;
}
print_field("Support: %s (0x%2.2x)", str, support);
}
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(uint8_t encr_mode)
{
const char *str;
switch (encr_mode) {
case 0x00:
str = "Disabled";
break;
case 0x01:
str = "Enabled";
break;
default:
str = "Reserved";
break;
}
print_field("Encryption: %s (0x%2.2x)", str, encr_mode);
}
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_short_range_mode(uint8_t mode)
{
const char *str;
switch (mode) {
case 0x00:
str = "Disabled";
break;
case 0x01:
str = "Enabled";
break;
default:
str = "Reserved";
break;
}
print_field("Short range mode: %s (0x%2.2x)", str, mode);
}
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(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("Event type: %s (0x%2.2x)", str, type);
}
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_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;
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" },
{ 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" },
{ }
};
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);
}
}