/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
 *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; 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 "parser.h"
#include "lib/hci.h"
#include "lib/hci_lib.h"
#include "lib/amp.h"

static uint16_t manufacturer = DEFAULT_COMPID;

static inline uint16_t get_manufacturer(void)
{
	return (manufacturer == DEFAULT_COMPID ? parser.defcompid : manufacturer);
}

#define EVENT_NUM 77
static char *event_str[EVENT_NUM + 1] = {
	"Unknown",
	"Inquiry Complete",
	"Inquiry Result",
	"Connect Complete",
	"Connect Request",
	"Disconn Complete",
	"Auth Complete",
	"Remote Name Req Complete",
	"Encrypt Change",
	"Change Connection Link Key Complete",
	"Master Link Key Complete",
	"Read Remote Supported Features",
	"Read Remote Ver Info Complete",
	"QoS Setup Complete",
	"Command Complete",
	"Command Status",
	"Hardware Error",
	"Flush Occurred",
	"Role Change",
	"Number of Completed Packets",
	"Mode Change",
	"Return Link Keys",
	"PIN Code Request",
	"Link Key Request",
	"Link Key Notification",
	"Loopback Command",
	"Data Buffer Overflow",
	"Max Slots Change",
	"Read Clock Offset Complete",
	"Connection Packet Type Changed",
	"QoS Violation",
	"Page Scan Mode Change",
	"Page Scan Repetition Mode Change",
	"Flow Specification Complete",
	"Inquiry Result with RSSI",
	"Read Remote Extended Features",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Synchronous Connect Complete",
	"Synchronous Connect Changed",
	"Sniff Subrate",
	"Extended Inquiry Result",
	"Encryption Key Refresh Complete",
	"IO Capability Request",
	"IO Capability Response",
	"User Confirmation Request",
	"User Passkey Request",
	"Remote OOB Data Request",
	"Simple Pairing Complete",
	"Unknown",
	"Link Supervision Timeout Change",
	"Enhanced Flush Complete",
	"Unknown",
	"User Passkey Notification",
	"Keypress Notification",
	"Remote Host Supported Features Notification",
	"LE Meta Event",
	"Unknown",
	"Physical Link Complete",
	"Channel Selected",
	"Disconnection Physical Link Complete",
	"Physical Link Loss Early Warning",
	"Physical Link Recovery",
	"Logical Link Complete",
	"Disconnection Logical Link Complete",
	"Flow Spec Modify Complete",
	"Number Of Completed Data Blocks",
	"AMP Start Test",
	"AMP Test End",
	"AMP Receiver Report",
	"Short Range Mode Change Complete",
	"AMP Status Change",
};

#define LE_EV_NUM 5
static char *ev_le_meta_str[LE_EV_NUM + 1] = {
	"Unknown",
	"LE Connection Complete",
	"LE Advertising Report",
	"LE Connection Update Complete",
	"LE Read Remote Used Features Complete",
	"LE Long Term Key Request",
};

#define CMD_LINKCTL_NUM 60
static char *cmd_linkctl_str[CMD_LINKCTL_NUM + 1] = {
	"Unknown",
	"Inquiry",
	"Inquiry Cancel",
	"Periodic Inquiry Mode",
	"Exit Periodic Inquiry Mode",
	"Create Connection",
	"Disconnect",
	"Add SCO Connection",
	"Create Connection Cancel",
	"Accept Connection Request",
	"Reject Connection Request",
	"Link Key Request Reply",
	"Link Key Request Negative Reply",
	"PIN Code Request Reply",
	"PIN Code Request Negative Reply",
	"Change Connection Packet Type",
	"Unknown",
	"Authentication Requested",
	"Unknown",
	"Set Connection Encryption",
	"Unknown",
	"Change Connection Link Key",
	"Unknown",
	"Master Link Key",
	"Unknown",
	"Remote Name Request",
	"Remote Name Request Cancel",
	"Read Remote Supported Features",
	"Read Remote Extended Features",
	"Read Remote Version Information",
	"Unknown",
	"Read Clock Offset",
	"Read LMP Handle",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Setup Synchronous Connection",
	"Accept Synchronous Connection",
	"Reject Synchronous Connection",
	"IO Capability Request Reply",
	"User Confirmation Request Reply",
	"User Confirmation Request Negative Reply",
	"User Passkey Request Reply",
	"User Passkey Request Negative Reply",
	"Remote OOB Data Request Reply",
	"Unknown",
	"Unknown",
	"Remote OOB Data Request Negative Reply",
	"IO Capability Request Negative Reply",
	"Create Physical Link",
	"Accept Physical Link",
	"Disconnect Physical Link",
	"Create Logical Link",
	"Accept Logical Link",
	"Disconnect Logical Link",
	"Logical Link Cancel",
	"Flow Spec Modify",
};

#define CMD_LINKPOL_NUM 17
static char *cmd_linkpol_str[CMD_LINKPOL_NUM + 1] = {
	"Unknown",
	"Hold Mode",
	"Unknown",
	"Sniff Mode",
	"Exit Sniff Mode",
	"Park State",
	"Exit Park State",
	"QoS Setup",
	"Unknown",
	"Role Discovery",
	"Unknown",
	"Switch Role",
	"Read Link Policy Settings",
	"Write Link Policy Settings",
	"Read Default Link Policy Settings",
	"Write Default Link Policy Settings",
	"Flow Specification",
	"Sniff Subrating",
};

#define CMD_HOSTCTL_NUM 109
static char *cmd_hostctl_str[CMD_HOSTCTL_NUM + 1] = {
	"Unknown",
	"Set Event Mask",
	"Unknown",
	"Reset",
	"Unknown",
	"Set Event Filter",
	"Unknown",
	"Unknown",
	"Flush",
	"Read PIN Type ",
	"Write PIN Type",
	"Create New Unit Key",
	"Unknown",
	"Read Stored Link Key",
	"Unknown",
	"Unknown",
	"Unknown",
	"Write Stored Link Key",
	"Delete Stored Link Key",
	"Write Local Name",
	"Read Local Name",
	"Read Connection Accept Timeout",
	"Write Connection Accept Timeout",
	"Read Page Timeout",
	"Write Page Timeout",
	"Read Scan Enable",
	"Write Scan Enable",
	"Read Page Scan Activity",
	"Write Page Scan Activity",
	"Read Inquiry Scan Activity",
	"Write Inquiry Scan Activity",
	"Read Authentication Enable",
	"Write Authentication Enable",
	"Read Encryption Mode",
	"Write Encryption Mode",
	"Read Class of Device",
	"Write Class of Device",
	"Read Voice Setting",
	"Write Voice Setting",
	"Read Automatic Flush Timeout",
	"Write Automatic Flush Timeout",
	"Read Num Broadcast Retransmissions",
	"Write Num Broadcast Retransmissions",
	"Read Hold Mode Activity ",
	"Write Hold Mode Activity",
	"Read Transmit Power Level",
	"Read Synchronous Flow Control Enable",
	"Write Synchronous Flow Control Enable",
	"Unknown",
	"Set Host Controller To Host Flow Control",
	"Unknown",
	"Host Buffer Size",
	"Unknown",
	"Host Number of Completed Packets",
	"Read Link Supervision Timeout",
	"Write Link Supervision Timeout",
	"Read Number of Supported IAC",
	"Read Current IAC LAP",
	"Write Current IAC LAP",
	"Read Page Scan Period Mode",
	"Write Page Scan Period Mode",
	"Read Page Scan Mode",
	"Write Page Scan Mode",
	"Set AFH Host Channel Classification",
	"Unknown",
	"Unknown",
	"Read Inquiry Scan Type",
	"Write Inquiry Scan Type",
	"Read Inquiry Mode",
	"Write Inquiry Mode",
	"Read Page Scan Type",
	"Write Page Scan Type",
	"Read AFH Channel Assessment Mode",
	"Write AFH Channel Assessment Mode",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Read Extended Inquiry Response",
	"Write Extended Inquiry Response",
	"Refresh Encryption Key",
	"Unknown",
	"Read Simple Pairing Mode",
	"Write Simple Pairing Mode",
	"Read Local OOB Data",
	"Read Inquiry Response Transmit Power Level",
	"Write Inquiry Transmit Power Level",
	"Read Default Erroneous Data Reporting",
	"Write Default Erroneous Data Reporting",
	"Unknown",
	"Unknown",
	"Unknown",
	"Enhanced Flush",
	"Unknown",
	"Read Logical Link Accept Timeout",
	"Write Logical Link Accept Timeout",
	"Set Event Mask Page 2",
	"Read Location Data",
	"Write Location Data",
	"Read Flow Control Mode",
	"Write Flow Control Mode",
	"Read Enhanced Transmit Power Level",
	"Read Best Effort Flush Timeout",
	"Write Best Effort Flush Timeout",
	"Short Range Mode",
	"Read LE Host Supported",
	"Write LE Host Supported",
};

#define CMD_INFO_NUM 10
static char *cmd_info_str[CMD_INFO_NUM + 1] = {
	"Unknown",
	"Read Local Version Information",
	"Read Local Supported Commands",
	"Read Local Supported Features",
	"Read Local Extended Features",
	"Read Buffer Size",
	"Unknown",
	"Read Country Code",
	"Unknown",
	"Read BD ADDR",
	"Read Data Block Size",
};

#define CMD_STATUS_NUM 11
static char *cmd_status_str[CMD_STATUS_NUM + 1] = {
	"Unknown",
	"Read Failed Contact Counter",
	"Reset Failed Contact Counter",
	"Read Link Quality",
	"Unknown",
	"Read RSSI",
	"Read AFH Channel Map",
	"Read Clock",
	"Read Encryption Key Size",
	"Read Local AMP Info",
	"Read Local AMP ASSOC",
	"Write Remote AMP ASSOC"
};

#define CMD_TESTING_NUM 4
static char *cmd_testing_str[CMD_TESTING_NUM + 1] = {
	"Unknown",
	"Read Loopback Mode",
	"Write Loopback Mode",
	"Enable Device Under Test mode",
	"Unknown",
};

#define CMD_LE_NUM 31
static char *cmd_le_str[CMD_LE_NUM + 1] = {
	"Unknown",
	"LE Set Event Mask",
	"LE Read Buffer Size",
	"LE Read Local Supported Features",
	"Unknown",
	"LE Set Random Address",
	"LE Set Advertising Parameters",
	"LE Read Advertising Channel Tx Power",
	"LE Set Advertising Data",
	"LE Set Scan Response Data",
	"LE Set Advertise Enable",
	"LE Set Scan Parameters",
	"LE Set Scan Enable",
	"LE Create Connection",
	"LE Create Connection Cancel",
	"LE Read White List Size",
	"LE Clear White List",
	"LE Add Device To White List",
	"LE Remove Device From White List",
	"LE Connection Update",
	"LE Set Host Channel Classification",
	"LE Read Channel Map",
	"LE Read Remote Used Features",
	"LE Encrypt",
	"LE Rand",
	"LE Start Encryption",
	"LE Long Term Key Request Reply",
	"LE Long Term Key Request Negative Reply",
	"LE Read Supported States",
	"LE Receiver Test",
	"LE Transmitter Test",
	"LE Test End",
};

#define ERROR_CODE_NUM 63
static char *error_code_str[ERROR_CODE_NUM + 1] = {
	"Success",
	"Unknown HCI Command",
	"Unknown Connection Identifier",
	"Hardware Failure",
	"Page Timeout",
	"Authentication Failure",
	"PIN or Key Missing",
	"Memory Capacity Exceeded",
	"Connection Timeout",
	"Connection Limit Exceeded",
	"Synchronous Connection to a Device Exceeded",
	"ACL Connection Already Exists",
	"Command Disallowed",
	"Connection Rejected due to Limited Resources",
	"Connection Rejected due to Security Reasons",
	"Connection Rejected due to Unacceptable BD_ADDR",
	"Connection Accept Timeout Exceeded",
	"Unsupported Feature or Parameter Value",
	"Invalid HCI Command Parameters",
	"Remote User Terminated Connection",
	"Remote Device Terminated Connection due to Low Resources",
	"Remote Device Terminated Connection due to Power Off",
	"Connection Terminated by Local Host",
	"Repeated Attempts",
	"Pairing Not Allowed",
	"Unknown LMP PDU",
	"Unsupported Remote Feature / Unsupported LMP Feature",
	"SCO Offset Rejected",
	"SCO Interval Rejected",
	"SCO Air Mode Rejected",
	"Invalid LMP Parameters / Invalid LL Parameters",
	"Unspecified Error",
	"Unsupported LMP Parameter Value / Unsupported LL Parameter Value",
	"Role Change Not Allowed",
	"LMP Response Timeout",
	"LMP Error Transaction Collision",
	"LMP PDU Not Allowed",
	"Encryption Mode Not Acceptable",
	"Link Key Can Not be Changed",
	"Requested QoS Not Supported",
	"Instant Passed",
	"Pairing with Unit Key Not Supported",
	"Different Transaction Collision",
	"Reserved",
	"QoS Unacceptable Parameter",
	"QoS Rejected",
	"Channel Classification Not Supported",
	"Insufficient Security",
	"Parameter out of Mandatory Range",
	"Reserved",
	"Role Switch Pending",
	"Reserved",
	"Reserved Slot Violation",
	"Role Switch Failed",
	"Extended Inquiry Response Too Large",
	"Simple Pairing Not Supported by Host",
	"Host Busy - Pairing",
	"Connection Rejected due to No Suitable Channel Found",
	"Controller Busy",
	"Unacceptable Connection Parameters",
	"Directed Advertising Timeout",
	"Connection Terminated Due to MIC Failure",
	"Connection Failed to be Established",
	"MAC Connection Failed",
};

static char *status2str(uint8_t status)
{
	char *str;

	if (status <= ERROR_CODE_NUM)
		str = error_code_str[status];
	else
		str = "Unknown";

	return str;
}

static char *opcode2str(uint16_t opcode)
{
	uint16_t ogf = cmd_opcode_ogf(opcode);
	uint16_t ocf = cmd_opcode_ocf(opcode);
	char *cmd;

	switch (ogf) {
	case OGF_INFO_PARAM:
		if (ocf <= CMD_INFO_NUM)
			cmd = cmd_info_str[ocf];
		else
			cmd = "Unknown";
		break;

	case OGF_HOST_CTL:
		if (ocf <= CMD_HOSTCTL_NUM)
			cmd = cmd_hostctl_str[ocf];
		else
			cmd = "Unknown";
		break;

	case OGF_LINK_CTL:
		if (ocf <= CMD_LINKCTL_NUM)
			cmd = cmd_linkctl_str[ocf];
		else
			cmd = "Unknown";
		break;

	case OGF_LINK_POLICY:
		if (ocf <= CMD_LINKPOL_NUM)
			cmd = cmd_linkpol_str[ocf];
		else
			cmd = "Unknown";
		break;

	case OGF_STATUS_PARAM:
		if (ocf <= CMD_STATUS_NUM)
			cmd = cmd_status_str[ocf];
		else
			cmd = "Unknown";
		break;

	case OGF_TESTING_CMD:
		if (ocf <= CMD_TESTING_NUM)
			cmd = cmd_testing_str[ocf];
		else
			cmd = "Unknown";
		break;

	case OGF_LE_CTL:
		if (ocf <= CMD_LE_NUM)
			cmd = cmd_le_str[ocf];
		else
			cmd = "Unknown";
		break;

	case OGF_VENDOR_CMD:
		cmd = "Vendor";
		break;

	default:
		cmd = "Unknown";
		break;
	}

	return cmd;
}

static char *linktype2str(uint8_t type)
{
	switch (type) {
	case 0x00:
		return "SCO";
	case 0x01:
		return "ACL";
	case 0x02:
		return "eSCO";
	default:
		return "Unknown";
	}
}

static char *role2str(uint8_t role)
{
	switch (role) {
	case 0x00:
		return "Master";
	case 0x01:
		return "Slave";
	default:
		return "Unknown";
	}
}

static char *mode2str(uint8_t mode)
{
	switch (mode) {
	case 0x00:
		return "Active";
	case 0x01:
		return "Hold";
	case 0x02:
		return "Sniff";
	case 0x03:
		return "Park";
	default:
		return "Unknown";
	}
}

static char *airmode2str(uint8_t mode)
{
	switch (mode) {
	case 0x00:
		return "u-law log";
	case 0x01:
		return "A-law log";
	case 0x02:
		return "CVSD";
	case 0x04:
		return "Transparent data";
	default:
		return "Reserved";
	}
}

static const char *bdaddrtype2str(uint8_t type)
{
	switch (type) {
	case 0x00:
		return "Public";
	case 0x01:
		return "Random";
	default:
		return "Reserved";
	}
}

static const char *evttype2str(uint8_t type)
{
	switch (type) {
	case 0x00:
		return "ADV_IND - Connectable undirected advertising";
	case 0x01:
		return "ADV_DIRECT_IND - Connectable directed advertising";
	case 0x02:
		return "ADV_SCAN_IND - Scannable undirected advertising";
	case 0x03:
		return "ADV_NONCONN_IND - Non connectable undirected advertising";
	case 0x04:
		return "SCAN_RSP - Scan Response";
	default:
		return "Reserved";
	}
}

static char *keytype2str(uint8_t type)
{
	switch (type) {
	case 0x00:
		return "Combination Key";
	case 0x01:
		return "Local Unit Key";
	case 0x02:
		return "Remote Unit Key";
	case 0x03:
		return "Debug Combination Key";
	case 0x04:
		return "Unauthenticated Combination Key";
	case 0x05:
		return "Authenticated Combination Key";
	case 0x06:
		return "Changed Combination Key";
	default:
		return "Reserved";
	}
}

static char *capability2str(uint8_t capability)
{
	switch (capability) {
	case 0x00:
		return "DisplayOnly";
	case 0x01:
		return "DisplayYesNo";
	case 0x02:
		return "KeyboardOnly";
	case 0x03:
		return "NoInputNoOutput";
	default:
		return "Reserved";
	}
}

static char *authentication2str(uint8_t authentication)
{
	switch (authentication) {
	case 0x00:
		return "No Bonding (No MITM Protection)";
	case 0x01:
		return "No Bonding (MITM Protection)";
	case 0x02:
		return "Dedicated Bonding (No MITM Protection)";
	case 0x03:
		return "Dedicated Bonding (MITM Protection)";
	case 0x04:
		return "General Bonding (No MITM Protection)";
	case 0x05:
		return "General Bonding (MITM Protection)";
	default:
		return "Reserved";
	}
}

static char *eventmask2str(const uint8_t mask[8])
{
	int i;

	for (i = 0; i < 7; i++) {
		if (mask[i] != 0x00)
			return "Reserved";
	}

	switch (mask[7]) {
	case 0x00:
		return "No LE events specified";
	case 0x01:
		return "LE Connection Complete Event";
	case 0x02:
		return "LE Advertising Report Event";
	case 0x04:
		return "LE Connection Update Complete Event";
	case 0x08:
		return "LE Read Remote Used Features Complete Event";
	case 0x10:
		return "LE Long Term Key Request Event";
	case 0x1F:
		return "Default";
	default:
		return "Reserved";
	}
}

static char *lefeatures2str(const uint8_t features[8])
{
	if (features[0] & 0x01)
		return "Link Layer supports LE Encryption";

	return "RFU";
}

static char *filterpolicy2str(uint8_t policy)
{
	switch (policy) {
	case 0x00:
		return "Allow scan from any, connection from any";
	case 0x01:
		return "Allow scan from white list, connection from any";
	case 0x02:
		return "Allow scan from any, connection from white list";
	case 0x03:
		return "Allow scan and connection from white list";
	default:
		return "Reserved";
	}
}

static inline void ext_inquiry_data_dump(int level, struct frame *frm,
						uint8_t *data)
{
	uint8_t len = data[0];
	uint8_t type;
	char *str;
	int i;

	if (len == 0)
		return;

	type = data[1];
	data += 2;
	len -= 1;

	switch (type) {
	case 0x01:
		p_indent(level, frm);
		printf("Flags:");
		for (i = 0; i < len; i++)
			printf(" 0x%2.2x", data[i]);
		printf("\n");
		break;

	case 0x02:
	case 0x03:
		p_indent(level, frm);
		printf("%s service classes:",
				type == 0x02 ? "Shortened" : "Complete");

		for (i = 0; i < len / 2; i++)
			printf(" 0x%4.4x", get_le16(data + i * 2));

		printf("\n");
		break;

	case 0x08:
	case 0x09:
		str = malloc(len + 1);
		if (str) {
			snprintf(str, len + 1, "%s", (char *) data);
			for (i = 0; i < len; i++)
				if (!isprint(str[i]))
					str[i] = '.';
			p_indent(level, frm);
			printf("%s local name: \'%s\'\n",
				type == 0x08 ? "Shortened" : "Complete", str);
			free(str);
		}
		break;

	case 0x0a:
		p_indent(level, frm);
		printf("TX power level: %d\n", *((uint8_t *) data));
		break;

	default:
		p_indent(level, frm);
		printf("Unknown type 0x%02x with %d bytes data\n",
							type, len);
		break;
	}
}

static inline void ext_inquiry_response_dump(int level, struct frame *frm)
{
	void *ptr = frm->ptr;
	uint32_t len = frm->len;
	uint8_t *data;
	uint8_t length;

	data = frm->ptr;
	length = get_u8(frm);

	while (length > 0) {
		ext_inquiry_data_dump(level, frm, data);

		frm->ptr += length;
		frm->len -= length;

		data = frm->ptr;
		length = get_u8(frm);
	}

	frm->ptr = ptr +
		(EXTENDED_INQUIRY_INFO_SIZE - INQUIRY_INFO_WITH_RSSI_SIZE);
	frm->len = len +
		(EXTENDED_INQUIRY_INFO_SIZE - INQUIRY_INFO_WITH_RSSI_SIZE);
}

static inline void bdaddr_command_dump(int level, struct frame *frm)
{
	bdaddr_t *bdaddr = frm->ptr;
	char addr[18];

	frm->ptr += sizeof(bdaddr_t);
	frm->len -= sizeof(bdaddr_t);

	p_indent(level, frm);
	p_ba2str(bdaddr, addr);
	printf("bdaddr %s\n", addr);

	raw_dump(level, frm);
}

static inline void generic_command_dump(int level, struct frame *frm)
{
	uint16_t handle = btohs(htons(get_u16(frm)));

	p_indent(level, frm);
	printf("handle %d\n", handle);

	raw_dump(level, frm);
}

static inline void generic_write_mode_dump(int level, struct frame *frm)
{
	uint8_t mode = get_u8(frm);

	p_indent(level, frm);
	printf("mode 0x%2.2x\n", mode);
}

static inline void inquiry_dump(int level, struct frame *frm)
{
	inquiry_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("lap 0x%2.2x%2.2x%2.2x len %d num %d\n",
		cp->lap[2], cp->lap[1], cp->lap[0], cp->length, cp->num_rsp);
}

static inline void periodic_inquiry_dump(int level, struct frame *frm)
{
	periodic_inquiry_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("max %d min %d lap 0x%2.2x%2.2x%2.2x len %d num %d\n",
		btohs(cp->max_period), btohs(cp->min_period),
		cp->lap[2], cp->lap[1], cp->lap[0], cp->length, cp->num_rsp);
}

static inline void create_conn_dump(int level, struct frame *frm)
{
	create_conn_cp *cp = frm->ptr;
	uint16_t ptype = btohs(cp->pkt_type);
	uint16_t clkoffset = btohs(cp->clock_offset);
	char addr[18], *str;

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s ptype 0x%4.4x rswitch 0x%2.2x clkoffset 0x%4.4x%s\n",
		addr, ptype, cp->role_switch,
		clkoffset & 0x7fff, clkoffset & 0x8000 ? " (valid)" : "");

	str = hci_ptypetostr(ptype);
	if (str) {
		p_indent(level, frm);
		printf("Packet type: %s\n", str);
		free(str);
	}
}

static inline void disconnect_dump(int level, struct frame *frm)
{
	disconnect_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d reason 0x%2.2x\n", btohs(cp->handle), cp->reason);

	p_indent(level, frm);
	printf("Reason: %s\n", status2str(cp->reason));
}

static inline void add_sco_dump(int level, struct frame *frm)
{
	add_sco_cp *cp = frm->ptr;
	uint16_t ptype = btohs(cp->pkt_type);
	char *str;

	p_indent(level, frm);
	printf("handle %d ptype 0x%4.4x\n", btohs(cp->handle), ptype);

	str = hci_ptypetostr(ptype);
	if (str) {
		p_indent(level, frm);
		printf("Packet type: %s\n", str);
		free(str);
	}
}

static inline void accept_conn_req_dump(int level, struct frame *frm)
{
	accept_conn_req_cp *cp = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s role 0x%2.2x\n", addr, cp->role);

	p_indent(level, frm);
	printf("Role: %s\n", role2str(cp->role));
}

static inline void reject_conn_req_dump(int level, struct frame *frm)
{
	reject_conn_req_cp *cp = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s reason 0x%2.2x\n", addr, cp->reason);

	p_indent(level, frm);
	printf("Reason: %s\n", status2str(cp->reason));
}

static inline void pin_code_reply_dump(int level, struct frame *frm)
{
	pin_code_reply_cp *cp = frm->ptr;
	char addr[18], pin[17];

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	memset(pin, 0, sizeof(pin));
	if (parser.flags & DUMP_NOVENDOR)
		memset(pin, '*', cp->pin_len);
	else
		memcpy(pin, cp->pin_code, cp->pin_len);
	printf("bdaddr %s len %d pin \'%s\'\n", addr, cp->pin_len, pin);
}

static inline void link_key_reply_dump(int level, struct frame *frm)
{
	link_key_reply_cp *cp = frm->ptr;
	char addr[18];
	int i;

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s key ", addr);
	for (i = 0; i < 16; i++)
		if (parser.flags & DUMP_NOVENDOR)
			printf("**");
		else
			printf("%2.2X", cp->link_key[i]);
	printf("\n");
}

static inline void pin_code_neg_reply_dump(int level, struct frame *frm)
{
	bdaddr_t *bdaddr = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(bdaddr, addr);
	printf("bdaddr %s\n", addr);
}

static inline void user_passkey_reply_dump(int level, struct frame *frm)
{
	user_passkey_reply_cp *cp = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s passkey %d\n", addr, btohl(cp->passkey));
}

static inline void remote_oob_data_reply_dump(int level, struct frame *frm)
{
	remote_oob_data_reply_cp *cp = frm->ptr;
	char addr[18];
	int i;

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s\n", addr);

	p_indent(level, frm);
	printf("hash 0x");
	for (i = 0; i < 16; i++)
		printf("%02x", cp->hash[i]);
	printf("\n");

	p_indent(level, frm);
	printf("randomizer 0x");
	for (i = 0; i < 16; i++)
			printf("%02x", cp->randomizer[i]);
	printf("\n");
}

static inline void io_capability_reply_dump(int level, struct frame *frm)
{
	io_capability_reply_cp *cp = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s capability 0x%2.2x oob 0x%2.2x auth 0x%2.2x\n",
					addr, cp->capability, cp->oob_data,
							cp->authentication);

	p_indent(level, frm);
	printf("Capability: %s (OOB data %s)\n",
			capability2str(cp->capability),
			cp->oob_data == 0x00 ? "not present" : "available");

	p_indent(level, frm);
	printf("Authentication: %s\n", authentication2str(cp->authentication));
}

static inline void set_conn_encrypt_dump(int level, struct frame *frm)
{
	set_conn_encrypt_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d encrypt 0x%2.2x\n", btohs(cp->handle), cp->encrypt);
}

static inline void remote_name_req_dump(int level, struct frame *frm)
{
	remote_name_req_cp *cp = frm->ptr;
	uint16_t clkoffset = btohs(cp->clock_offset);
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s mode %d clkoffset 0x%4.4x%s\n",
		addr, cp->pscan_rep_mode,
		clkoffset & 0x7fff, clkoffset & 0x8000 ? " (valid)" : "");
}

static inline void master_link_key_dump(int level, struct frame *frm)
{
	master_link_key_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("flag %d\n", cp->key_flag);
}

static inline void read_remote_ext_features_dump(int level, struct frame *frm)
{
	read_remote_ext_features_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d page %d\n", btohs(cp->handle), cp->page_num);
}

static inline void setup_sync_conn_dump(int level, struct frame *frm)
{
	setup_sync_conn_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d voice setting 0x%4.4x ptype 0x%4.4x\n",
		btohs(cp->handle), btohs(cp->voice_setting),
		btohs(cp->pkt_type));
}

static inline void create_physical_link_dump(int level, struct frame *frm)
{
	create_physical_link_cp *cp = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("phy handle 0x%2.2x key length %d key type %d\n",
		cp->handle, cp->key_length, cp->key_type);
	p_indent(level, frm);
	printf("key ");
	for (i = 0; i < cp->key_length && cp->key_length <= 32; i++)
		printf("%2.2x", cp->key[i]);
	printf("\n");
}

static inline void create_logical_link_dump(int level, struct frame *frm)
{
	create_logical_link_cp *cp = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("phy handle 0x%2.2x\n", cp->handle);

	p_indent(level, frm);
	printf("tx_flow ");
	for (i = 0; i < 16; i++)
		printf("%2.2x", cp->tx_flow[i]);
	printf("\n");

	p_indent(level, frm);
	printf("rx_flow ");
	for (i = 0; i < 16; i++)
		printf("%2.2x", cp->rx_flow[i]);
	printf("\n");
}

static inline void hold_mode_dump(int level, struct frame *frm)
{
	hold_mode_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d max %d min %d\n", btohs(cp->handle),
			btohs(cp->max_interval), btohs(cp->min_interval));
}

static inline void sniff_mode_dump(int level, struct frame *frm)
{
	sniff_mode_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d max %d min %d attempt %d timeout %d\n",
		btohs(cp->handle), btohs(cp->max_interval),
		btohs(cp->min_interval), btohs(cp->attempt), btohs(cp->timeout));
}

static inline void qos_setup_dump(int level, struct frame *frm)
{
	qos_setup_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d flags 0x%2.2x\n", btohs(cp->handle), cp->flags);

	p_indent(level, frm);
	printf("Service type: %d\n", cp->qos.service_type);
	p_indent(level, frm);
	printf("Token rate: %d\n", btohl(cp->qos.token_rate));
	p_indent(level, frm);
	printf("Peak bandwith: %d\n", btohl(cp->qos.peak_bandwidth));
	p_indent(level, frm);
	printf("Latency: %d\n", btohl(cp->qos.latency));
	p_indent(level, frm);
	printf("Delay variation: %d\n", btohl(cp->qos.delay_variation));
}

static inline void write_link_policy_dump(int level, struct frame *frm)
{
	write_link_policy_cp *cp = frm->ptr;
	uint16_t policy = btohs(cp->policy);
	char *str;

	p_indent(level, frm);
	printf("handle %d policy 0x%2.2x\n", btohs(cp->handle), policy);

	str = hci_lptostr(policy);
	if (str) {
		p_indent(level, frm);
		printf("Link policy: %s\n", str);
		free(str);
	}
}

static inline void write_default_link_policy_dump(int level, struct frame *frm)
{
	uint16_t policy = btohs(htons(get_u16(frm)));
	char *str;

	p_indent(level, frm);
	printf("policy 0x%2.2x\n", policy);

	str = hci_lptostr(policy);
	if (str) {
		p_indent(level, frm);
		printf("Link policy: %s\n", str);
		free(str);
	}
}

static inline void sniff_subrating_dump(int level, struct frame *frm)
{
	sniff_subrating_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d\n", btohs(cp->handle));

	p_indent(level, frm);
	printf("max latency %d\n", btohs(cp->max_latency));

	p_indent(level, frm);
	printf("min timeout remote %d local %d\n",
		btohs(cp->min_remote_timeout), btohs(cp->min_local_timeout));
}

static inline void set_event_mask_dump(int level, struct frame *frm)
{
	set_event_mask_cp *cp = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("Mask: 0x");
	for (i = 0; i < 8; i++)
		printf("%2.2x", cp->mask[i]);
	printf("\n");
}

static inline void set_event_flt_dump(int level, struct frame *frm)
{
	set_event_flt_cp *cp = frm->ptr;
	uint8_t dev_class[3], dev_mask[3];
	char addr[18];

	p_indent(level, frm);
	printf("type %d condition %d\n", cp->flt_type,
				(cp->flt_type == 0) ? 0 : cp->cond_type);

	switch (cp->flt_type) {
	case FLT_CLEAR_ALL:
		p_indent(level, frm);
		printf("Clear all filters\n");
		break;
	case FLT_INQ_RESULT:
		p_indent(level, frm);
		printf("Inquiry result");
		switch (cp->cond_type) {
		case INQ_RESULT_RETURN_ALL:
			printf(" for all devices\n");
			break;
		case INQ_RESULT_RETURN_CLASS:
			memcpy(dev_class, cp->condition, 3);
			memcpy(dev_mask, cp->condition + 3, 3);
			printf(" with class 0x%2.2x%2.2x%2.2x mask 0x%2.2x%2.2x%2.2x\n",
				dev_class[2], dev_class[1], dev_class[0],
				dev_mask[2], dev_mask[1], dev_mask[0]);
			break;
		case INQ_RESULT_RETURN_BDADDR:
			p_ba2str((bdaddr_t *) cp->condition, addr);
			printf(" with bdaddr %s\n", addr);
			break;
		default:
			printf("\n");
			break;
		}
		break;
	case FLT_CONN_SETUP:
		p_indent(level, frm);
		printf("Connection setup");
		switch (cp->cond_type) {
		case CONN_SETUP_ALLOW_ALL:
		case CONN_SETUP_ALLOW_CLASS:
		case CONN_SETUP_ALLOW_BDADDR:
		default:
			printf("\n");
			break;
		}
		break;
	}
}

static inline void write_pin_type_dump(int level, struct frame *frm)
{
	write_pin_type_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("type %d\n", cp->pin_type);
}

static inline void request_stored_link_key_dump(int level, struct frame *frm)
{
	read_stored_link_key_cp *cp = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s all %d\n", addr, cp->read_all);
}

static inline void return_link_keys_dump(int level, struct frame *frm)
{
	uint8_t num = get_u8(frm);
	uint8_t key[16];
	char addr[18];
	int i, n;

	for (n = 0; n < num; n++) {
		p_ba2str(frm->ptr, addr);
		memcpy(key, frm->ptr + 6, 16);

		p_indent(level, frm);
		printf("bdaddr %s key ", addr);
		for (i = 0; i < 16; i++)
			if (parser.flags & DUMP_NOVENDOR)
				printf("**");
			else
				printf("%2.2X", key[i]);
		printf("\n");

		frm->ptr += 2;
		frm->len -= 2;
	}
}

static inline void change_local_name_dump(int level, struct frame *frm)
{
	change_local_name_cp *cp = frm->ptr;
	char name[249];
	int i;

	memset(name, 0, sizeof(name));
	for (i = 0; i < 248 && cp->name[i]; i++)
		if (isprint(cp->name[i]))
			name[i] = cp->name[i];
		else
			name[i] = '.';

	p_indent(level, frm);
	printf("name \'%s\'\n", name);
}

static inline void write_class_of_dev_dump(int level, struct frame *frm)
{
	write_class_of_dev_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("class 0x%2.2x%2.2x%2.2x\n",
			cp->dev_class[2], cp->dev_class[1], cp->dev_class[0]);
}

static inline void write_voice_setting_dump(int level, struct frame *frm)
{
	write_voice_setting_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("voice setting 0x%4.4x\n", btohs(cp->voice_setting));
}

static inline void write_current_iac_lap_dump(int level, struct frame *frm)
{
	write_current_iac_lap_cp *cp = frm->ptr;
	int i;

	for (i = 0; i < cp->num_current_iac; i++) {
		p_indent(level, frm);
		printf("IAC 0x%2.2x%2.2x%2.2x", cp->lap[i][2], cp->lap[i][1], cp->lap[i][0]);
		if (cp->lap[i][2] == 0x9e && cp->lap[i][1] == 0x8b) {
			switch (cp->lap[i][0]) {
			case 0x00:
				printf(" (Limited Inquiry Access Code)");
				break;
			case 0x33:
				printf(" (General Inquiry Access Code)");
				break;
			}
		}
		printf("\n");
	}
}

static inline void write_scan_enable_dump(int level, struct frame *frm)
{
	uint8_t enable = get_u8(frm);

	p_indent(level, frm);
	printf("enable %d\n", enable);
}

static inline void write_page_timeout_dump(int level, struct frame *frm)
{
	write_page_timeout_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("timeout %d\n", btohs(cp->timeout));
}

static inline void write_page_activity_dump(int level, struct frame *frm)
{
	write_page_activity_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("interval %d window %d\n", btohs(cp->interval), btohs(cp->window));
}

static inline void write_inquiry_scan_type_dump(int level, struct frame *frm)
{
	write_inquiry_scan_type_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("type %d\n", cp->type);
}

static inline void write_inquiry_mode_dump(int level, struct frame *frm)
{
	write_inquiry_mode_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("mode %d\n", cp->mode);
}

static inline void set_afh_classification_dump(int level, struct frame *frm)
{
	set_afh_classification_cp *cp = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("map 0x");
	for (i = 0; i < 10; i++)
		printf("%02x", cp->map[i]);
	printf("\n");
}

static inline void write_link_supervision_timeout_dump(int level, struct frame *frm)
{
	write_link_supervision_timeout_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d timeout %d\n",
				btohs(cp->handle), btohs(cp->timeout));
}

static inline void write_ext_inquiry_response_dump(int level, struct frame *frm)
{
	write_ext_inquiry_response_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("fec 0x%2.2x\n", cp->fec);

	frm->ptr++;
	frm->len--;

	ext_inquiry_response_dump(level, frm);
}

static inline void write_inquiry_transmit_power_level_dump(int level, struct frame *frm)
{
	write_inquiry_transmit_power_level_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("level %d\n", cp->level);
}

static inline void write_default_error_data_reporting_dump(int level, struct frame *frm)
{
	write_default_error_data_reporting_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("reporting %d\n", cp->reporting);
}

static inline void enhanced_flush_dump(int level, struct frame *frm)
{
	enhanced_flush_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d type %d\n", btohs(cp->handle), cp->type);
}

static inline void send_keypress_notify_dump(int level, struct frame *frm)
{
	send_keypress_notify_cp *cp = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s type %d\n", addr, cp->type);
}

static inline void request_transmit_power_level_dump(int level, struct frame *frm)
{
	read_transmit_power_level_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d type %d (%s)\n",
					btohs(cp->handle), cp->type,
					cp->type ? "maximum" : "current");
}

static inline void request_local_ext_features_dump(int level, struct frame *frm)
{
	read_local_ext_features_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("page %d\n", cp->page_num);
}

static inline void request_clock_dump(int level, struct frame *frm)
{
	read_clock_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle %d which %d (%s)\n",
					btohs(cp->handle), cp->which_clock,
					cp->which_clock ? "piconet" : "local");
}

static inline void host_buffer_size_dump(int level, struct frame *frm)
{
	host_buffer_size_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("ACL MTU %d:%d SCO MTU %d:%d\n",
				btohs(cp->acl_mtu), btohs(cp->acl_max_pkt),
				cp->sco_mtu, btohs(cp->sco_max_pkt));
}

static inline void num_comp_pkts_dump(int level, struct frame *frm)
{
	uint8_t num = get_u8(frm);
	uint16_t handle, packets;
	int i;

	for (i = 0; i < num; i++) {
		handle = btohs(htons(get_u16(frm)));
		packets = btohs(htons(get_u16(frm)));

		p_indent(level, frm);
		printf("handle %d packets %d\n", handle, packets);
	}
}

static inline void le_create_connection_dump(int level, struct frame *frm)
{
	char addr[18];
	le_create_connection_cp *cp = frm->ptr;

	p_indent(level, frm);
	p_ba2str(&cp->peer_bdaddr, addr);
	printf("bdaddr %s type %d\n", addr, cp->peer_bdaddr_type);
	p_indent(level, frm);
	printf("interval %u window %u initiator_filter %u\n",
		btohs(cp->interval), btohs(cp->window), cp->initiator_filter);
	p_indent(level, frm);
	printf("own_bdaddr_type %u min_interval %u max_interval %u\n",
			cp->own_bdaddr_type, btohs(cp->min_interval),
			btohs(cp->max_interval));
	p_indent(level, frm);
	printf("latency %u supervision_to %u min_ce %u max_ce %u\n",
			btohs(cp->latency), btohs(cp->supervision_timeout),
			btohs(cp->min_ce_length), btohs(cp->max_ce_length));
}

static inline void le_set_event_mask_dump(int level, struct frame *frm)
{
	int i;
	le_set_event_mask_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("mask 0x");
	for (i = 0; i < 8; i++)
		printf("%.2x", cp->mask[i]);

	printf(" (%s)\n", eventmask2str(cp->mask));
}

static inline void le_set_random_address_dump(int level, struct frame *frm)
{
	char addr[18];
	le_set_random_address_cp *cp = frm->ptr;

	p_indent(level, frm);
	p_ba2str(&cp->bdaddr, addr);
	printf("bdaddr %s\n", addr);
}


static inline void le_set_advertising_parameters_dump(int level, struct frame *frm)
{
	char addr[18];
	le_set_advertising_parameters_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("min %.3fms, max %.3fms\n", btohs(cp->min_interval) * 0.625,
			btohs(cp->max_interval) * 0.625);

	p_indent(level, frm);
	printf("type 0x%02x (%s) ownbdaddr 0x%02x (%s)\n", cp->advtype,
			evttype2str(cp->advtype), cp->own_bdaddr_type,
			bdaddrtype2str(cp->own_bdaddr_type));

	p_indent(level, frm);
	p_ba2str(&cp->direct_bdaddr, addr);
	printf("directbdaddr 0x%02x (%s) %s\n", cp->direct_bdaddr_type,
			bdaddrtype2str(cp->direct_bdaddr_type), addr);

	p_indent(level, frm);
	printf("channelmap 0x%02x filterpolicy 0x%02x (%s)\n",
			cp->chan_map, cp->filter, filterpolicy2str(cp->filter));
}

static inline void le_set_scan_parameters_dump(int level, struct frame *frm)
{
	le_set_scan_parameters_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("type 0x%02x (%s)\n", cp->type,
		cp->type == 0x00 ? "passive" : "active");

	p_indent(level, frm);
	printf("interval %.3fms window %.3fms\n", btohs(cp->interval) * 0.625,
		btohs(cp->window) * 0.625);

	p_indent(level, frm);
	printf("own address: 0x%02x (%s) policy: %s\n", cp->own_bdaddr_type,
			bdaddrtype2str(cp->own_bdaddr_type),
		(cp->filter == 0x00 ? "All" :
			(cp->filter == 0x01 ? "white list only" : "reserved")));
}

static inline void le_set_scan_enable_dump(int level, struct frame *frm)
{
	le_set_scan_enable_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("value 0x%02x (%s)\n", cp->enable,
		(cp->enable == 0x00 ? "scanning disabled" :
		"scanning enabled"));

	p_indent(level, frm);
	printf("filter duplicates 0x%02x (%s)\n", cp->filter_dup,
		(cp->filter_dup == 0x00 ? "disabled" : "enabled"));
}

static inline void write_remote_amp_assoc_cmd_dump(int level,
							struct frame *frm)
{
	write_remote_amp_assoc_cp *cp = frm->ptr;

	p_indent(level, frm);
	printf("handle 0x%2.2x len_so_far %d remaining_len %d\n", cp->handle,
				cp->length_so_far, cp->remaining_length);

	amp_assoc_dump(level + 1, cp->fragment, frm->len - 5);
}

static inline void command_dump(int level, struct frame *frm)
{
	hci_command_hdr *hdr = frm->ptr;
	uint16_t opcode = btohs(hdr->opcode);
	uint16_t ogf = cmd_opcode_ogf(opcode);
	uint16_t ocf = cmd_opcode_ocf(opcode);

	if (p_filter(FILT_HCI))
		return;

	if (ogf == OGF_VENDOR_CMD && (parser.flags & DUMP_NOVENDOR))
		return;

	p_indent(level, frm);
	printf("HCI Command: %s (0x%2.2x|0x%4.4x) plen %d\n", 
				opcode2str(opcode), ogf, ocf, hdr->plen);

	frm->ptr += HCI_COMMAND_HDR_SIZE;
	frm->len -= HCI_COMMAND_HDR_SIZE;

	if (ogf == OGF_VENDOR_CMD) {
		if (ocf == 0 && get_manufacturer() == 10) {
			csr_dump(level + 1, frm);
			return;
		}
	}

	if (!(parser.flags & DUMP_VERBOSE)) {
		raw_dump(level, frm);
		return;
	}

	switch (ogf) {
	case OGF_LINK_CTL:
		switch (ocf) {
		case OCF_INQUIRY:
			inquiry_dump(level + 1, frm);
			return;
		case OCF_PERIODIC_INQUIRY:
			periodic_inquiry_dump(level + 1, frm);
			return;
		case OCF_INQUIRY_CANCEL:
		case OCF_EXIT_PERIODIC_INQUIRY:
			return;
		case OCF_CREATE_CONN:
			create_conn_dump(level + 1, frm);
			return;
		case OCF_DISCONNECT:
			disconnect_dump(level + 1, frm);
			return;
		case OCF_CREATE_CONN_CANCEL:
		case OCF_REMOTE_NAME_REQ_CANCEL:
		case OCF_ACCEPT_SYNC_CONN_REQ:
			bdaddr_command_dump(level + 1, frm);
			return;
		case OCF_ADD_SCO:
		case OCF_SET_CONN_PTYPE:
			add_sco_dump(level + 1, frm);
			return;
		case OCF_ACCEPT_CONN_REQ:
			accept_conn_req_dump(level + 1, frm);
			return;
		case OCF_REJECT_CONN_REQ:
		case OCF_REJECT_SYNC_CONN_REQ:
		case OCF_IO_CAPABILITY_NEG_REPLY:
			reject_conn_req_dump(level + 1, frm);
			return;
		case OCF_PIN_CODE_REPLY:
			pin_code_reply_dump(level + 1, frm);
			return;
		case OCF_LINK_KEY_REPLY:
			link_key_reply_dump(level + 1, frm);
			return;
		case OCF_PIN_CODE_NEG_REPLY:
		case OCF_LINK_KEY_NEG_REPLY:
		case OCF_USER_CONFIRM_REPLY:
		case OCF_USER_CONFIRM_NEG_REPLY:
		case OCF_USER_PASSKEY_NEG_REPLY:
		case OCF_REMOTE_OOB_DATA_NEG_REPLY:
			pin_code_neg_reply_dump(level + 1, frm);
			return;
		case OCF_USER_PASSKEY_REPLY:
			user_passkey_reply_dump(level + 1, frm);
			return;
		case OCF_REMOTE_OOB_DATA_REPLY:
			remote_oob_data_reply_dump(level + 1, frm);
			return;
		case OCF_IO_CAPABILITY_REPLY:
			io_capability_reply_dump(level + 1, frm);
			return;
		case OCF_SET_CONN_ENCRYPT:
			set_conn_encrypt_dump(level + 1, frm);
			return;
		case OCF_AUTH_REQUESTED:
		case OCF_CHANGE_CONN_LINK_KEY:
		case OCF_READ_REMOTE_FEATURES:
		case OCF_READ_REMOTE_VERSION:
		case OCF_READ_CLOCK_OFFSET:
		case OCF_READ_LMP_HANDLE:
		case OCF_DISCONNECT_LOGICAL_LINK:
			generic_command_dump(level + 1, frm);
			return;
		case OCF_MASTER_LINK_KEY:
			master_link_key_dump(level + 1, frm);
			return;
		case OCF_READ_REMOTE_EXT_FEATURES:
			read_remote_ext_features_dump(level + 1, frm);
			return;
		case OCF_REMOTE_NAME_REQ:
			remote_name_req_dump(level + 1, frm);
			return;
		case OCF_SETUP_SYNC_CONN:
			setup_sync_conn_dump(level + 1, frm);
			return;
		case OCF_CREATE_PHYSICAL_LINK:
		case OCF_ACCEPT_PHYSICAL_LINK:
			create_physical_link_dump(level + 1, frm);
			return;
		case OCF_CREATE_LOGICAL_LINK:
		case OCF_ACCEPT_LOGICAL_LINK:
			create_logical_link_dump(level + 1, frm);
			return;
		}
		break;

	case OGF_LINK_POLICY:
		switch (ocf) {
		case OCF_HOLD_MODE:
		case OCF_PARK_MODE:
			hold_mode_dump(level + 1, frm);
			return;
		case OCF_SNIFF_MODE:
			sniff_mode_dump(level + 1, frm);
			return;
		case OCF_EXIT_SNIFF_MODE:
		case OCF_EXIT_PARK_MODE:
		case OCF_ROLE_DISCOVERY:
		case OCF_READ_LINK_POLICY:
			generic_command_dump(level + 1, frm);
			return;
		case OCF_READ_DEFAULT_LINK_POLICY:
			return;
		case OCF_SWITCH_ROLE:
			accept_conn_req_dump(level + 1, frm);
			return;
		case OCF_QOS_SETUP:
			qos_setup_dump(level + 1, frm);
			return;
		case OCF_WRITE_LINK_POLICY:
			write_link_policy_dump(level + 1, frm);
			return;
		case OCF_WRITE_DEFAULT_LINK_POLICY:
			write_default_link_policy_dump(level + 1, frm);
			return;
		case OCF_SNIFF_SUBRATING:
			sniff_subrating_dump(level + 1, frm);
			return;
		}
		break;

	case OGF_HOST_CTL:
		switch (ocf) {
		case OCF_RESET:
		case OCF_CREATE_NEW_UNIT_KEY:
			return;
		case OCF_SET_EVENT_MASK:
		case OCF_SET_EVENT_MASK_PAGE_2:
			set_event_mask_dump(level + 1, frm);
			return;
		case OCF_SET_EVENT_FLT:
			set_event_flt_dump(level + 1, frm);
			return;
		case OCF_WRITE_PIN_TYPE:
			write_pin_type_dump(level + 1, frm);
			return;
		case OCF_READ_STORED_LINK_KEY:
		case OCF_DELETE_STORED_LINK_KEY:
			request_stored_link_key_dump(level + 1, frm);
			return;
		case OCF_WRITE_STORED_LINK_KEY:
			return_link_keys_dump(level + 1, frm);
			return;
		case OCF_CHANGE_LOCAL_NAME:
			change_local_name_dump(level + 1, frm);
			return;
		case OCF_WRITE_CLASS_OF_DEV:
			write_class_of_dev_dump(level + 1, frm);
			return;
		case OCF_WRITE_VOICE_SETTING:
			write_voice_setting_dump(level + 1, frm);
			return;
		case OCF_WRITE_CURRENT_IAC_LAP:
			write_current_iac_lap_dump(level + 1, frm);
			return;
		case OCF_WRITE_SCAN_ENABLE:
		case OCF_WRITE_AUTH_ENABLE:
		case OCF_SET_CONTROLLER_TO_HOST_FC:
			write_scan_enable_dump(level + 1, frm);
			return;
		case OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT:
		case OCF_WRITE_CONN_ACCEPT_TIMEOUT:
		case OCF_WRITE_PAGE_TIMEOUT:
			write_page_timeout_dump(level + 1, frm);
			return;
		case OCF_WRITE_PAGE_ACTIVITY:
		case OCF_WRITE_INQ_ACTIVITY:
			write_page_activity_dump(level + 1, frm);
			return;
		case OCF_WRITE_INQUIRY_SCAN_TYPE:
			write_inquiry_scan_type_dump(level + 1, frm);
			return;
		case OCF_WRITE_ENCRYPT_MODE:
		case OCF_WRITE_INQUIRY_MODE:
		case OCF_WRITE_AFH_MODE:
			write_inquiry_mode_dump(level + 1, frm);
			return;
		case OCF_SET_AFH_CLASSIFICATION:
			set_afh_classification_dump(level + 1, frm);
			return;
		case OCF_READ_TRANSMIT_POWER_LEVEL:
			request_transmit_power_level_dump(level + 1, frm);
			return;
		case OCF_HOST_BUFFER_SIZE:
			host_buffer_size_dump(level + 1, frm);
			return;
		case OCF_HOST_NUM_COMP_PKTS:
			num_comp_pkts_dump(level + 1, frm);
			return;
		case OCF_FLUSH:
		case OCF_READ_LINK_SUPERVISION_TIMEOUT:
		case OCF_REFRESH_ENCRYPTION_KEY:
		case OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT:
			generic_command_dump(level + 1, frm);
			return;
		case OCF_WRITE_LINK_SUPERVISION_TIMEOUT:
			write_link_supervision_timeout_dump(level + 1, frm);
			return;
		case OCF_WRITE_EXT_INQUIRY_RESPONSE:
			write_ext_inquiry_response_dump(level + 1, frm);
			return;
		case OCF_WRITE_SIMPLE_PAIRING_MODE:
		case OCF_WRITE_FLOW_CONTROL_MODE:
			generic_write_mode_dump(level + 1, frm);
			return;
		case OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL:
			write_inquiry_transmit_power_level_dump(level + 1, frm);
			return;
		case OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING:
			write_default_error_data_reporting_dump(level + 1, frm);
			return;
		case OCF_ENHANCED_FLUSH:
			enhanced_flush_dump(level + 1, frm);
			return;
		case OCF_SEND_KEYPRESS_NOTIFY:
			send_keypress_notify_dump(level + 1, frm);
			return;
		}
		break;

	case OGF_INFO_PARAM:
		switch (ocf) {
		case OCF_READ_LOCAL_EXT_FEATURES:
			request_local_ext_features_dump(level + 1, frm);
			return;
		}
		break;

	case OGF_STATUS_PARAM:
		switch (ocf) {
		case OCF_READ_LINK_QUALITY:
		case OCF_READ_RSSI:
		case OCF_READ_AFH_MAP:
			generic_command_dump(level + 1, frm);
			return;
		case OCF_READ_CLOCK:
			request_clock_dump(level + 1, frm);
			return;
		case OCF_WRITE_REMOTE_AMP_ASSOC:
			write_remote_amp_assoc_cmd_dump(level + 1, frm);
			return;
		}
		break;

	case OGF_TESTING_CMD:
		switch (ocf) {
		case OCF_WRITE_LOOPBACK_MODE:
		case OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE:
			generic_write_mode_dump(level + 1, frm);
			return;
		}
		break;

	case OGF_LE_CTL:
		switch (ocf) {
		case OCF_LE_SET_EVENT_MASK:
			le_set_event_mask_dump(level + 1, frm);
			return;
		case OCF_LE_READ_BUFFER_SIZE:
		case OCF_LE_READ_LOCAL_SUPPORTED_FEATURES:
		case OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER:
			return;
		case OCF_LE_SET_RANDOM_ADDRESS:
			le_set_random_address_dump(level + 1, frm);
			return;
		case OCF_LE_SET_ADVERTISING_PARAMETERS:
			le_set_advertising_parameters_dump(level + 1, frm);
			return;
		case OCF_LE_SET_SCAN_PARAMETERS:
			le_set_scan_parameters_dump(level + 1, frm);
			return;
		case OCF_LE_SET_SCAN_ENABLE:
			le_set_scan_enable_dump(level + 1, frm);
			return;
		case OCF_LE_CREATE_CONN:
			le_create_connection_dump(level + 1, frm);
			return;
		}
		break;
	}

	raw_dump(level, frm);
}

static inline void status_response_dump(int level, struct frame *frm)
{
	uint8_t status = get_u8(frm);

	p_indent(level, frm);
	printf("status 0x%2.2x\n", status);

	if (status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(status));
	}

	raw_dump(level, frm);
}

static inline void handle_response_dump(int level, struct frame *frm)
{
	uint16_t handle = btohs(htons(get_u16(frm)));

	p_indent(level, frm);
	printf("handle %d\n", handle);

	raw_dump(level, frm);
}

static inline void bdaddr_response_dump(int level, struct frame *frm)
{
	uint8_t status = get_u8(frm);
	bdaddr_t *bdaddr = frm->ptr;
	char addr[18];

	frm->ptr += sizeof(bdaddr_t);
	frm->len -= sizeof(bdaddr_t);

	p_indent(level, frm);
	p_ba2str(bdaddr, addr);
	printf("status 0x%2.2x bdaddr %s\n", status, addr);

	if (status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(status));
	}

	raw_dump(level, frm);
}

static inline void read_data_block_size_dump(int level, struct frame *frm)
{
	read_data_block_size_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x\n", rp->status);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		p_indent(level, frm);
		printf("Max ACL %d Block len %d Num blocks %d\n",
			btohs(rp->max_acl_len), btohs(rp->data_block_len),
							btohs(rp->num_blocks));
	}
}

static inline void generic_response_dump(int level, struct frame *frm)
{
	uint8_t status = get_u8(frm);
	uint16_t handle = btohs(htons(get_u16(frm)));

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d\n", status, handle);

	if (status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(status));
	}

	raw_dump(level, frm);
}

static inline void status_mode_dump(int level, struct frame *frm)
{
	uint8_t status = get_u8(frm);
	uint8_t mode = get_u8(frm);

	p_indent(level, frm);
	printf("status 0x%2.2x mode 0x%2.2x\n", status, mode);

	if (status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(status));
	}
}

static inline void read_link_policy_dump(int level, struct frame *frm)
{
	read_link_policy_rp *rp = frm->ptr;
	uint16_t policy = btohs(rp->policy);
	char *str;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d policy 0x%2.2x\n",
				rp->status, btohs(rp->handle), policy);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		str = hci_lptostr(policy);
		if (str) {
			p_indent(level, frm);
			printf("Link policy: %s\n", str);
			free(str);
		}
	}
}

static inline void read_default_link_policy_dump(int level, struct frame *frm)
{
	uint8_t status = get_u8(frm);
	uint16_t policy = btohs(htons(get_u16(frm)));
	char *str;

	p_indent(level, frm);
	printf("status 0x%2.2x policy 0x%2.2x\n", status, policy);

	if (status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(status));
	} else {
		str = hci_lptostr(policy);
		if (str) {
			p_indent(level, frm);
			printf("Link policy: %s\n", str);
			free(str);
		}
	}
}

static inline void read_pin_type_dump(int level, struct frame *frm)
{
	read_pin_type_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x type %d\n", rp->status, rp->pin_type);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_stored_link_key_dump(int level, struct frame *frm)
{
	read_stored_link_key_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x max %d num %d\n",
				rp->status, rp->max_keys, rp->num_keys);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void write_stored_link_key_dump(int level, struct frame *frm)
{
	write_stored_link_key_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x written %d\n", rp->status, rp->num_keys);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void delete_stored_link_key_dump(int level, struct frame *frm)
{
	delete_stored_link_key_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x deleted %d\n", rp->status, btohs(rp->num_keys));

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_local_name_dump(int level, struct frame *frm)
{
	read_local_name_rp *rp = frm->ptr;
	char name[249];
	int i;

	memset(name, 0, sizeof(name));
	for (i = 0; i < 248 && rp->name[i]; i++)
		if (isprint(rp->name[i]))
			name[i] = rp->name[i];
		else
			name[i] = '.';

	p_indent(level, frm);
	printf("status 0x%2.2x name \'%s\'\n", rp->status, name);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_class_of_dev_dump(int level, struct frame *frm)
{
	read_class_of_dev_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x class 0x%2.2x%2.2x%2.2x\n", rp->status,
			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_voice_setting_dump(int level, struct frame *frm)
{
	read_voice_setting_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x voice setting 0x%4.4x\n",
					rp->status, btohs(rp->voice_setting));

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_current_iac_lap_dump(int level, struct frame *frm)
{
	read_current_iac_lap_rp *rp = frm->ptr;
	int i;

	for (i = 0; i < rp->num_current_iac; i++) {
		p_indent(level, frm);
		printf("IAC 0x%2.2x%2.2x%2.2x", rp->lap[i][2], rp->lap[i][1], rp->lap[i][0]);
		if (rp->lap[i][2] == 0x9e && rp->lap[i][1] == 0x8b) {
			switch (rp->lap[i][0]) {
			case 0x00:
				printf(" (Limited Inquiry Access Code)");
				break;
			case 0x33:
				printf(" (General Inquiry Access Code)");
				break;
			}
		}
		printf("\n");
	}
}

static inline void read_scan_enable_dump(int level, struct frame *frm)
{
	uint8_t status = get_u8(frm);
	uint8_t enable = get_u8(frm);

	p_indent(level, frm);
	printf("status 0x%2.2x enable %d\n", status, enable);

	if (status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(status));
	}
}

static inline void read_page_timeout_dump(int level, struct frame *frm)
{
	read_page_timeout_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x timeout %d\n", rp->status, btohs(rp->timeout));

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_page_activity_dump(int level, struct frame *frm)
{
	read_page_activity_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x interval %d window %d\n",
			rp->status, btohs(rp->interval), btohs(rp->window));

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_inquiry_scan_type_dump(int level, struct frame *frm)
{
	read_inquiry_scan_type_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x type %d\n", rp->status, rp->type);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_inquiry_mode_dump(int level, struct frame *frm)
{
	read_inquiry_mode_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x mode %d\n", rp->status, rp->mode);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_link_supervision_timeout_dump(int level, struct frame *frm)
{
	read_link_supervision_timeout_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d timeout %d\n",
			rp->status, btohs(rp->handle), btohs(rp->timeout));

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_transmit_power_level_dump(int level, struct frame *frm)
{
	read_transmit_power_level_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d level %d\n",
				rp->status, btohs(rp->handle), rp->level);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_ext_inquiry_response_dump(int level, struct frame *frm)
{
	read_ext_inquiry_response_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x fec 0x%2.2x\n", rp->status, rp->fec);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		frm->ptr += 2;
		frm->len -= 2;

		ext_inquiry_response_dump(level, frm);
	}
}

static inline void read_inquiry_transmit_power_level_dump(int level, struct frame *frm)
{
	read_inquiry_transmit_power_level_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x level %d\n", rp->status, rp->level);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_default_error_data_reporting_dump(int level, struct frame *frm)
{
	read_default_error_data_reporting_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x reporting %d\n", rp->status, rp->reporting);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_local_oob_data_dump(int level, struct frame *frm)
{
	read_local_oob_data_rp *rp = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("status 0x%2.2x\n", rp->status);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		p_indent(level, frm);
		printf("hash 0x");
		for (i = 0; i < 16; i++)
			printf("%02x", rp->hash[i]);
		printf("\n");

		p_indent(level, frm);
		printf("randomizer 0x");
		for (i = 0; i < 16; i++)
			printf("%02x", rp->randomizer[i]);
		printf("\n");
	}
}

static inline void read_local_version_dump(int level, struct frame *frm)
{
	read_local_version_rp *rp = frm->ptr;
	uint16_t manufacturer = btohs(rp->manufacturer);

	p_indent(level, frm);
	printf("status 0x%2.2x\n", rp->status);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		char *lmpver = lmp_vertostr(rp->lmp_ver);
		char *hciver = hci_vertostr(rp->hci_ver);

		p_indent(level, frm);
		printf("HCI Version: %s (0x%x) HCI Revision: 0x%x\n",
					hciver ? hciver : "n/a",
					rp->hci_ver, btohs(rp->hci_rev));
		p_indent(level, frm);
		printf("LMP Version: %s (0x%x) LMP Subversion: 0x%x\n",
					lmpver ? lmpver : "n/a",
					rp->lmp_ver, btohs(rp->lmp_subver));
		p_indent(level, frm);
		printf("Manufacturer: %s (%d)\n",
				bt_compidtostr(manufacturer), manufacturer);

		if (lmpver)
			free(lmpver);
		if (hciver)
			free(hciver);
	}
}

static inline void read_local_commands_dump(int level, struct frame *frm)
{
	read_local_commands_rp *rp = frm->ptr;
	int i, max = 0;

	p_indent(level, frm);
	printf("status 0x%2.2x\n", rp->status);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		for (i = 0; i < 64; i++)
			if (rp->commands[i])
				max = i + 1;
		p_indent(level, frm);
		printf("Commands: ");
		for (i = 0; i < (max > 32 ? 32 : max); i++)
			printf("%2.2x", rp->commands[i]);
		printf("\n");
		if (max > 32) {
			p_indent(level, frm);
			printf("          ");
			for (i = 32; i < max; i++)
				printf("%2.2x", rp->commands[i]);
			printf("\n");
		}
	}
}

static inline void read_local_features_dump(int level, struct frame *frm)
{
	read_local_features_rp *rp = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("status 0x%2.2x\n", rp->status);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		p_indent(level, frm);
		printf("Features:");
		for (i = 0; i < 8; i++)
			printf(" 0x%2.2x", rp->features[i]);
		printf("\n");
	}
}

static inline void read_local_ext_features_dump(int level, struct frame *frm)
{
	read_local_ext_features_rp *rp = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("status 0x%2.2x page %d max %d\n",
		rp->status, rp->page_num, rp->max_page_num);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		p_indent(level, frm);
		printf("Features:");
		for (i = 0; i < 8; i++)
			 printf(" 0x%2.2x", rp->features[i]);
		printf("\n");
	}
}

static inline void read_buffer_size_dump(int level, struct frame *frm)
{
	read_buffer_size_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x\n", rp->status);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		p_indent(level, frm);
		printf("ACL MTU %d:%d SCO MTU %d:%d\n",
				btohs(rp->acl_mtu), btohs(rp->acl_max_pkt),
				rp->sco_mtu, btohs(rp->sco_max_pkt));
	}
}

static inline void read_link_quality_dump(int level, struct frame *frm)
{
	read_link_quality_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d lq %d\n",
			rp->status, btohs(rp->handle), rp->link_quality);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_rssi_dump(int level, struct frame *frm)
{
	read_rssi_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d rssi %d\n",
				rp->status, btohs(rp->handle), rp->rssi);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_afh_map_dump(int level, struct frame *frm)
{
	read_afh_map_rp *rp = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d mode %d\n",
				rp->status, btohs(rp->handle), rp->mode);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		p_indent(level, frm);
		printf("AFH map: 0x");
		for (i = 0; i < 10; i++)
			printf("%2.2x", rp->map[i]);
		printf("\n");
	}
}

static inline void read_clock_dump(int level, struct frame *frm)
{
	read_clock_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d clock 0x%4.4x accuracy %d\n",
					rp->status, btohs(rp->handle),
					btohl(rp->clock), btohs(rp->accuracy));

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void read_local_amp_info_dump(int level, struct frame *frm)
{
	read_local_amp_info_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x amp status 0x%2.2x\n",
			rp->status, rp->amp_status);
	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		p_indent(level, frm);
		printf("total bandwidth %d, max guaranteed bandwidth %d\n",
			btohl(rp->total_bandwidth),
			btohl(rp->max_guaranteed_bandwidth));
		p_indent(level, frm);
		printf("min latency %d, max PDU %d, controller type 0x%2.2x\n",
			btohl(rp->min_latency), btohl(rp->max_pdu_size),
			rp->controller_type);
		p_indent(level, frm);
		printf("pal caps 0x%4.4x, max assoc len %d\n",
			btohs(rp->pal_caps), btohs(rp->max_amp_assoc_length));
		p_indent(level, frm);
		printf("max flush timeout %d, best effort flush timeout %d\n",
			btohl(rp->max_flush_timeout),
			btohl(rp->best_effort_flush_timeout));
	}
}

static inline void read_local_amp_assoc_dump(int level, struct frame *frm)
{
	read_local_amp_assoc_rp *rp = frm->ptr;
	uint16_t len = btohs(rp->length);

	p_indent(level, frm);
	printf("status 0x%2.2x handle 0x%2.2x remaining len %d\n",
			rp->status, rp->handle, len);
	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	} else {
		amp_assoc_dump(level + 1, rp->fragment, len);
	}
}

static inline void write_remote_amp_assoc_dump(int level, struct frame *frm)
{
	write_remote_amp_assoc_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle 0x%2.2x\n", rp->status, rp->handle);
	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void le_read_buffer_size_response_dump(int level, struct frame *frm)
{
	le_read_buffer_size_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x pktlen 0x%4.4x maxpkt 0x%2.2x\n", rp->status,
			rp->pkt_len, rp->max_pkt);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void le_read_local_supported_features_dump(int level, struct frame *frm)
{
	int i;
	le_read_local_supported_features_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x features 0x", rp->status);
	for (i = 0; i < 8; i++)
		printf("%2.2x", rp->features[i]);
	printf(" (%s)\n", lefeatures2str(rp->features));

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void le_read_advertising_channel_tx_power_dump(int level, struct frame *frm)
{
	le_read_advertising_channel_tx_power_rp *rp = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x level 0x%x (dBm)\n", rp->status, rp->level);

	if (rp->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(rp->status));
	}
}

static inline void cmd_complete_dump(int level, struct frame *frm)
{
	evt_cmd_complete *evt = frm->ptr;
	uint16_t opcode = btohs(evt->opcode);
	uint16_t ogf = cmd_opcode_ogf(opcode);
	uint16_t ocf = cmd_opcode_ocf(opcode);

	if (ogf == OGF_VENDOR_CMD && (parser.flags & DUMP_NOVENDOR))
		return;

	p_indent(level, frm);
	printf("%s (0x%2.2x|0x%4.4x) ncmd %d\n",
				opcode2str(opcode), ogf, ocf, evt->ncmd);

	frm->ptr += EVT_CMD_COMPLETE_SIZE;
	frm->len -= EVT_CMD_COMPLETE_SIZE;

	if (!(parser.flags & DUMP_VERBOSE)) {
		raw_dump(level, frm);
		return;
	}

	switch (ogf) {
	case OGF_LINK_CTL:
		switch (ocf) {
		case OCF_INQUIRY_CANCEL:
		case OCF_PERIODIC_INQUIRY:
		case OCF_EXIT_PERIODIC_INQUIRY:
		case OCF_READ_REMOTE_EXT_FEATURES:
			status_response_dump(level, frm);
			return;
		case OCF_CREATE_CONN_CANCEL:
		case OCF_REMOTE_NAME_REQ_CANCEL:
		case OCF_PIN_CODE_REPLY:
		case OCF_LINK_KEY_REPLY:
		case OCF_PIN_CODE_NEG_REPLY:
		case OCF_LINK_KEY_NEG_REPLY:
		case OCF_USER_CONFIRM_REPLY:
		case OCF_USER_CONFIRM_NEG_REPLY:
		case OCF_USER_PASSKEY_REPLY:
		case OCF_USER_PASSKEY_NEG_REPLY:
		case OCF_REMOTE_OOB_DATA_REPLY:
		case OCF_REMOTE_OOB_DATA_NEG_REPLY:
		case OCF_IO_CAPABILITY_REPLY:
		case OCF_IO_CAPABILITY_NEG_REPLY:
			bdaddr_response_dump(level, frm);
			return;
		}
		break;

	case OGF_LINK_POLICY:
		switch (ocf) {
		case OCF_READ_LINK_POLICY:
			read_link_policy_dump(level, frm);
			return;
		case OCF_WRITE_LINK_POLICY:
		case OCF_SNIFF_SUBRATING:
			generic_response_dump(level, frm);
			return;
		case OCF_READ_DEFAULT_LINK_POLICY:
			read_default_link_policy_dump(level, frm);
			return;
		case OCF_WRITE_DEFAULT_LINK_POLICY:
			status_response_dump(level, frm);
			return;
		}
		break;

	case OGF_HOST_CTL:
		switch (ocf) {
		case OCF_READ_PIN_TYPE:
			read_pin_type_dump(level, frm);
			return;
		case OCF_READ_STORED_LINK_KEY:
			read_stored_link_key_dump(level, frm);
			return;
		case OCF_WRITE_STORED_LINK_KEY:
			write_stored_link_key_dump(level, frm);
			return;
		case OCF_DELETE_STORED_LINK_KEY:
			delete_stored_link_key_dump(level, frm);
			return;
		case OCF_READ_LOCAL_NAME:
			read_local_name_dump(level, frm);
			return;
		case OCF_READ_CLASS_OF_DEV:
			read_class_of_dev_dump(level, frm);
			return;
		case OCF_READ_VOICE_SETTING:
			read_voice_setting_dump(level, frm);
			return;
		case OCF_READ_CURRENT_IAC_LAP:
			read_current_iac_lap_dump(level, frm);
			return;
		case OCF_READ_SCAN_ENABLE:
		case OCF_READ_AUTH_ENABLE:
			read_scan_enable_dump(level, frm);
			return;
		case OCF_READ_CONN_ACCEPT_TIMEOUT:
		case OCF_READ_PAGE_TIMEOUT:
		case OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT:
			read_page_timeout_dump(level, frm);
			return;
		case OCF_READ_PAGE_ACTIVITY:
		case OCF_READ_INQ_ACTIVITY:
			read_page_activity_dump(level, frm);
			return;
		case OCF_READ_INQUIRY_SCAN_TYPE:
			read_inquiry_scan_type_dump(level, frm);
			return;
		case OCF_READ_ENCRYPT_MODE:
		case OCF_READ_INQUIRY_MODE:
		case OCF_READ_AFH_MODE:
			read_inquiry_mode_dump(level, frm);
			return;
		case OCF_READ_LINK_SUPERVISION_TIMEOUT:
			read_link_supervision_timeout_dump(level, frm);
			return;
		case OCF_READ_TRANSMIT_POWER_LEVEL:
			read_transmit_power_level_dump(level, frm);
			return;
		case OCF_READ_EXT_INQUIRY_RESPONSE:
			read_ext_inquiry_response_dump(level, frm);
			return;
		case OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL:
			read_inquiry_transmit_power_level_dump(level, frm);
			return;
		case OCF_READ_DEFAULT_ERROR_DATA_REPORTING:
			read_default_error_data_reporting_dump(level, frm);
			return;
		case OCF_READ_LOCAL_OOB_DATA:
			read_local_oob_data_dump(level, frm);
			return;
		case OCF_READ_SIMPLE_PAIRING_MODE:
		case OCF_READ_FLOW_CONTROL_MODE:
			status_mode_dump(level, frm);
			return;
		case OCF_FLUSH:
		case OCF_WRITE_LINK_SUPERVISION_TIMEOUT:
			generic_response_dump(level, frm);
			return;
		case OCF_RESET:
		case OCF_SET_EVENT_MASK:
		case OCF_SET_EVENT_FLT:
		case OCF_WRITE_PIN_TYPE:
		case OCF_CREATE_NEW_UNIT_KEY:
		case OCF_CHANGE_LOCAL_NAME:
		case OCF_WRITE_CLASS_OF_DEV:
		case OCF_WRITE_VOICE_SETTING:
		case OCF_WRITE_CURRENT_IAC_LAP:
		case OCF_WRITE_SCAN_ENABLE:
		case OCF_WRITE_AUTH_ENABLE:
		case OCF_WRITE_ENCRYPT_MODE:
		case OCF_WRITE_CONN_ACCEPT_TIMEOUT:
		case OCF_WRITE_PAGE_TIMEOUT:
		case OCF_WRITE_PAGE_ACTIVITY:
		case OCF_WRITE_INQ_ACTIVITY:
		case OCF_WRITE_INQUIRY_SCAN_TYPE:
		case OCF_WRITE_INQUIRY_MODE:
		case OCF_WRITE_AFH_MODE:
		case OCF_SET_AFH_CLASSIFICATION:
		case OCF_WRITE_EXT_INQUIRY_RESPONSE:
		case OCF_WRITE_SIMPLE_PAIRING_MODE:
		case OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL:
		case OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING:
		case OCF_SET_CONTROLLER_TO_HOST_FC:
		case OCF_HOST_BUFFER_SIZE:
		case OCF_REFRESH_ENCRYPTION_KEY:
		case OCF_SEND_KEYPRESS_NOTIFY:
		case OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT:
		case OCF_SET_EVENT_MASK_PAGE_2:
		case OCF_WRITE_LOCATION_DATA:
		case OCF_WRITE_FLOW_CONTROL_MODE:
		case OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT:
		case OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT:
			status_response_dump(level, frm);
			return;
		}
		break;

	case OGF_INFO_PARAM:
		switch (ocf) {
		case OCF_READ_LOCAL_VERSION:
			read_local_version_dump(level, frm);
			return;
		case OCF_READ_LOCAL_COMMANDS:
			read_local_commands_dump(level, frm);
			return;
		case OCF_READ_LOCAL_FEATURES:
			read_local_features_dump(level, frm);
			return;
		case OCF_READ_LOCAL_EXT_FEATURES:
			read_local_ext_features_dump(level, frm);
			return;
		case OCF_READ_BUFFER_SIZE:
			read_buffer_size_dump(level, frm);
			return;
		case OCF_READ_BD_ADDR:
			bdaddr_response_dump(level, frm);
			return;
		case OCF_READ_DATA_BLOCK_SIZE:
			read_data_block_size_dump(level, frm);
			return;
		}
		break;

	case OGF_STATUS_PARAM:
		switch (ocf) {
		case OCF_READ_FAILED_CONTACT_COUNTER:
		case OCF_RESET_FAILED_CONTACT_COUNTER:
			status_response_dump(level, frm);
			return;
		case OCF_READ_LINK_QUALITY:
			read_link_quality_dump(level, frm);
			return;
		case OCF_READ_RSSI:
			read_rssi_dump(level, frm);
			return;
		case OCF_READ_AFH_MAP:
			read_afh_map_dump(level, frm);
			return;
		case OCF_READ_CLOCK:
			read_clock_dump(level, frm);
			return;
		case OCF_READ_LOCAL_AMP_INFO:
			read_local_amp_info_dump(level, frm);
			return;
		case OCF_READ_LOCAL_AMP_ASSOC:
			read_local_amp_assoc_dump(level, frm);
			return;
		case OCF_WRITE_REMOTE_AMP_ASSOC:
			write_remote_amp_assoc_dump(level, frm);
			return;
		}
		break;

	case OGF_TESTING_CMD:
		switch (ocf) {
		case OCF_READ_LOOPBACK_MODE:
			status_mode_dump(level, frm);
			return;
		case OCF_WRITE_LOOPBACK_MODE:
		case OCF_ENABLE_DEVICE_UNDER_TEST_MODE:
		case OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE:
			status_response_dump(level, frm);
			return;
		}
		break;

	case OGF_LE_CTL:
		switch (ocf) {
		case OCF_LE_SET_EVENT_MASK:
		case OCF_LE_SET_RANDOM_ADDRESS:
		case OCF_LE_SET_ADVERTISING_PARAMETERS:
		case OCF_LE_SET_ADVERTISING_DATA:
		case OCF_LE_SET_SCAN_RESPONSE_DATA:
		case OCF_LE_SET_ADVERTISE_ENABLE:
		case OCF_LE_SET_SCAN_PARAMETERS:
		case OCF_LE_SET_SCAN_ENABLE:
		case OCF_LE_CREATE_CONN:
		case OCF_LE_CLEAR_WHITE_LIST:
		case OCF_LE_ADD_DEVICE_TO_WHITE_LIST:
		case OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST:
		case OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION:
		case OCF_LE_RECEIVER_TEST:
		case OCF_LE_TRANSMITTER_TEST:
			status_response_dump(level, frm);
			return;
		case OCF_LE_READ_BUFFER_SIZE:
			le_read_buffer_size_response_dump(level, frm);
			return;
		case OCF_LE_READ_LOCAL_SUPPORTED_FEATURES:
			le_read_local_supported_features_dump(level, frm);
			return;
		case OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER:
			le_read_advertising_channel_tx_power_dump(level, frm);
			return;
		}
		break;
	}

	raw_dump(level, frm);
}

static inline void cmd_status_dump(int level, struct frame *frm)
{
	evt_cmd_status *evt = frm->ptr;
	uint16_t opcode = btohs(evt->opcode);
	uint16_t ogf = cmd_opcode_ogf(opcode);
	uint16_t ocf = cmd_opcode_ocf(opcode);

	if (ogf == OGF_VENDOR_CMD && (parser.flags & DUMP_NOVENDOR))
		return;

	p_indent(level, frm);
	printf("%s (0x%2.2x|0x%4.4x) status 0x%2.2x ncmd %d\n",
			opcode2str(opcode), ogf, ocf, evt->status, evt->ncmd);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void hardware_error_dump(int level, struct frame *frm)
{
	evt_hardware_error *evt = frm->ptr;

	p_indent(level, frm);
	printf("code %d\n", evt->code);
}

static inline void inq_result_dump(int level, struct frame *frm)
{
	uint8_t num = get_u8(frm);
	char addr[18];
	int i;

	for (i = 0; i < num; i++) {
		inquiry_info *info = frm->ptr;

		p_ba2str(&info->bdaddr, addr);

		p_indent(level, frm);
		printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x\n",
			addr, info->pscan_rep_mode, btohs(info->clock_offset),
			info->dev_class[2], info->dev_class[1], info->dev_class[0]);

		frm->ptr += INQUIRY_INFO_SIZE;
		frm->len -= INQUIRY_INFO_SIZE;
	}
}

static inline void conn_complete_dump(int level, struct frame *frm)
{
	evt_conn_complete *evt = frm->ptr;
	char addr[18];

	p_ba2str(&evt->bdaddr, addr);

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d bdaddr %s type %s encrypt 0x%2.2x\n",
			evt->status, btohs(evt->handle), addr,
			linktype2str(evt->link_type), evt->encr_mode);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void conn_request_dump(int level, struct frame *frm)
{
	evt_conn_request *evt = frm->ptr;
	char addr[18];

	p_ba2str(&evt->bdaddr, addr);

	p_indent(level, frm);
	printf("bdaddr %s class 0x%2.2x%2.2x%2.2x type %s\n",
			addr, evt->dev_class[2], evt->dev_class[1],
			evt->dev_class[0], linktype2str(evt->link_type));
}

static inline void disconn_complete_dump(int level, struct frame *frm)
{
	evt_disconn_complete *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d reason 0x%2.2x\n",
				evt->status, btohs(evt->handle), evt->reason);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else if (evt->reason > 0) {
		p_indent(level, frm);
		printf("Reason: %s\n", status2str(evt->reason));
	}
}

static inline void remote_name_req_complete_dump(int level, struct frame *frm)
{
	evt_remote_name_req_complete *evt = frm->ptr;
	char addr[18], name[249];
	int i;

	p_ba2str(&evt->bdaddr, addr);

	memset(name, 0, sizeof(name));
	for (i = 0; i < 248 && evt->name[i]; i++)
		if (isprint(evt->name[i]))
			name[i] = evt->name[i];
		else
			name[i] = '.';

	p_indent(level, frm);
	printf("status 0x%2.2x bdaddr %s name '%s'\n", evt->status, addr, name);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void master_link_key_complete_dump(int level, struct frame *frm)
{
	evt_master_link_key_complete *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d flag %d\n",
				evt->status, btohs(evt->handle), evt->key_flag);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void encrypt_change_dump(int level, struct frame *frm)
{
	evt_encrypt_change *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d encrypt 0x%2.2x\n",
				evt->status, btohs(evt->handle), evt->encrypt);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void read_remote_features_complete_dump(int level, struct frame *frm)
{
	evt_read_remote_features_complete *evt = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle));

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		p_indent(level, frm);
		printf("Features:");
		for (i = 0; i < 8; i++)
			printf(" 0x%2.2x", evt->features[i]);
		printf("\n");
	}
}

static inline void read_remote_version_complete_dump(int level, struct frame *frm)
{
	evt_read_remote_version_complete *evt = frm->ptr;
	uint16_t manufacturer = btohs(evt->manufacturer);

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle));

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		char *lmpver = lmp_vertostr(evt->lmp_ver);

		p_indent(level, frm);
		printf("LMP Version: %s (0x%x) LMP Subversion: 0x%x\n",
			lmpver ? lmpver : "n/a", evt->lmp_ver,
			btohs(evt->lmp_subver));
		p_indent(level, frm);
		printf("Manufacturer: %s (%d)\n",
			bt_compidtostr(manufacturer), manufacturer);

		if (lmpver)
			free(lmpver);
	}
}

static inline void qos_setup_complete_dump(int level, struct frame *frm)
{
	evt_qos_setup_complete *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d flags %d\n",
				evt->status, btohs(evt->handle), evt->flags);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		p_indent(level, frm);
		printf("Service type: %d\n", evt->qos.service_type);
		p_indent(level, frm);
		printf("Token rate: %d\n", btohl(evt->qos.token_rate));
		p_indent(level, frm);
		printf("Peak bandwith: %d\n", btohl(evt->qos.peak_bandwidth));
		p_indent(level, frm);
		printf("Latency: %d\n", btohl(evt->qos.latency));
		p_indent(level, frm);
		printf("Delay variation: %d\n", btohl(evt->qos.delay_variation));
	}
}

static inline void role_change_dump(int level, struct frame *frm)
{
	evt_role_change *evt = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&evt->bdaddr, addr);
	printf("status 0x%2.2x bdaddr %s role 0x%2.2x\n",
						evt->status, addr, evt->role);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		p_indent(level, frm);
		printf("Role: %s\n", role2str(evt->role));
	}
}

static inline void mode_change_dump(int level, struct frame *frm)
{
	evt_mode_change *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d mode 0x%2.2x interval %d\n",
		evt->status, btohs(evt->handle), evt->mode, btohs(evt->interval));

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		p_indent(level, frm);
		printf("Mode: %s\n", mode2str(evt->mode));
	}
}

static inline void pin_code_req_dump(int level, struct frame *frm)
{
	evt_pin_code_req *evt = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&evt->bdaddr, addr);
	printf("bdaddr %s\n", addr);
}

static inline void link_key_notify_dump(int level, struct frame *frm)
{
	evt_link_key_notify *evt = frm->ptr;
	char addr[18];
	int i;

	p_indent(level, frm);
	p_ba2str(&evt->bdaddr, addr);
	printf("bdaddr %s key ", addr);
	for (i = 0; i < 16; i++)
		if (parser.flags & DUMP_NOVENDOR)
			printf("**");
		else
			printf("%2.2X", evt->link_key[i]);
	printf(" type %d\n", evt->key_type);

	p_indent(level, frm);
	printf("Type: %s\n", keytype2str(evt->key_type));
}

static inline void max_slots_change_dump(int level, struct frame *frm)
{
	evt_max_slots_change *evt = frm->ptr;

	p_indent(level, frm);
	printf("handle %d slots %d\n", btohs(evt->handle), evt->max_slots);
}

static inline void data_buffer_overflow_dump(int level, struct frame *frm)
{
	evt_data_buffer_overflow *evt = frm->ptr;

	p_indent(level, frm);
	printf("type %s\n", linktype2str(evt->link_type));
}

static inline void read_clock_offset_complete_dump(int level, struct frame *frm)
{
	evt_read_clock_offset_complete *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d clkoffset 0x%4.4x\n",
		evt->status, btohs(evt->handle), btohs(evt->clock_offset));

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void conn_ptype_changed_dump(int level, struct frame *frm)
{
	evt_conn_ptype_changed *evt = frm->ptr;
	uint16_t ptype = btohs(evt->ptype);
	char *str;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d ptype 0x%4.4x\n",
				evt->status, btohs(evt->handle), ptype);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		str = hci_ptypetostr(ptype);
		if (str) {
			p_indent(level, frm);
			printf("Packet type: %s\n", str);
			free(str);
		}
	}
}

static inline void pscan_rep_mode_change_dump(int level, struct frame *frm)
{
	evt_pscan_rep_mode_change *evt = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&evt->bdaddr, addr);
	printf("bdaddr %s mode %d\n", addr, evt->pscan_rep_mode);
}

static inline void flow_spec_complete_dump(int level, struct frame *frm)
{
	evt_flow_spec_complete *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle 0x%4.4x flags %d %s\n",
				evt->status, btohs(evt->handle), evt->flags,
				evt->direction == 0 ? "outgoing" : "incoming");

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		p_indent(level, frm);
		printf("Service type: %d\n", evt->qos.service_type);
		p_indent(level, frm);
		printf("Token rate: %d\n", btohl(evt->qos.token_rate));
		p_indent(level, frm);
		printf("Peak bandwith: %d\n", btohl(evt->qos.peak_bandwidth));
		p_indent(level, frm);
		printf("Latency: %d\n", btohl(evt->qos.latency));
		p_indent(level, frm);
		printf("Delay variation: %d\n", btohl(evt->qos.delay_variation));
	}
}

static inline void inq_result_with_rssi_dump(int level, struct frame *frm)
{
	uint8_t num = get_u8(frm);
	char addr[18];
	int i;

	if (!num)
		return;

	if (frm->len / num == INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE) {
		for (i = 0; i < num; i++) {
			inquiry_info_with_rssi_and_pscan_mode *info = frm->ptr;

			p_indent(level, frm);

			p_ba2str(&info->bdaddr, addr);
			printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x rssi %d\n",
				addr, info->pscan_rep_mode, btohs(info->clock_offset),
				info->dev_class[2], info->dev_class[1], info->dev_class[0], info->rssi);

			frm->ptr += INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE;
			frm->len -= INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE;
		}
	} else {
		for (i = 0; i < num; i++) {
			inquiry_info_with_rssi *info = frm->ptr;

			p_indent(level, frm);

			p_ba2str(&info->bdaddr, addr);
			printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x rssi %d\n",
				addr, info->pscan_rep_mode, btohs(info->clock_offset),
				info->dev_class[2], info->dev_class[1], info->dev_class[0], info->rssi);

			frm->ptr += INQUIRY_INFO_WITH_RSSI_SIZE;
			frm->len -= INQUIRY_INFO_WITH_RSSI_SIZE;
		}
	}
}

static inline void read_remote_ext_features_complete_dump(int level, struct frame *frm)
{
	evt_read_remote_ext_features_complete *evt = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d page %d max %d\n",
					evt->status, btohs(evt->handle),
					evt->page_num, evt->max_page_num);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		p_indent(level, frm);
		printf("Features:");
		for (i = 0; i < 8; i++)
			printf(" 0x%2.2x", evt->features[i]);
		printf("\n");
	}
}

static inline void sync_conn_complete_dump(int level, struct frame *frm)
{
	evt_sync_conn_complete *evt = frm->ptr;
	char addr[18];

	p_ba2str(&evt->bdaddr, addr);

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d bdaddr %s type %s\n",
					evt->status, btohs(evt->handle), addr,
					evt->link_type == 0 ? "SCO" : "eSCO");

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		p_indent(level, frm);
		printf("Air mode: %s\n", airmode2str(evt->air_mode));
	}
}

static inline void sync_conn_changed_dump(int level, struct frame *frm)
{
	evt_sync_conn_changed *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle));

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void sniff_subrating_event_dump(int level, struct frame *frm)
{
	evt_sniff_subrating *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle));

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else {
		p_indent(level, frm);
		printf("max latency transmit %d receive %d\n",
					btohs(evt->max_tx_latency),
					btohs(evt->max_rx_latency));

		p_indent(level, frm);
		printf("min timeout remote %d local %d\n",
					btohs(evt->min_remote_timeout),
					btohs(evt->min_local_timeout));
	}
}

static inline void extended_inq_result_dump(int level, struct frame *frm)
{
	uint8_t num = get_u8(frm);
	char addr[18];
	int i;

	for (i = 0; i < num; i++) {
		extended_inquiry_info *info = frm->ptr;

		p_ba2str(&info->bdaddr, addr);

		p_indent(level, frm);
		printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x rssi %d\n",
			addr, info->pscan_rep_mode, btohs(info->clock_offset),
			info->dev_class[2], info->dev_class[1], info->dev_class[0], info->rssi);

		frm->ptr += INQUIRY_INFO_WITH_RSSI_SIZE;
		frm->len -= INQUIRY_INFO_WITH_RSSI_SIZE;

		ext_inquiry_response_dump(level, frm);
	}
}

static inline void link_supervision_timeout_changed_dump(int level, struct frame *frm)
{
	evt_link_supervision_timeout_changed *evt = frm->ptr;

	p_indent(level, frm);
	printf("handle %d timeout %d\n",
				btohs(evt->handle), btohs(evt->timeout));
}

static inline void user_passkey_notify_dump(int level, struct frame *frm)
{
	evt_user_passkey_notify *evt = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&evt->bdaddr, addr);
	printf("bdaddr %s passkey %d\n", addr, btohl(evt->passkey));
}

static inline void keypress_notify_dump(int level, struct frame *frm)
{
	evt_keypress_notify *evt = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	p_ba2str(&evt->bdaddr, addr);
	printf("bdaddr %s type %d\n", addr, evt->type);
}

static inline void remote_host_features_notify_dump(int level, struct frame *frm)
{
	evt_remote_host_features_notify *evt = frm->ptr;
	char addr[18];
	int i;

	p_indent(level, frm);
	p_ba2str(&evt->bdaddr, addr);
	printf("bdaddr %s\n", addr);

	p_indent(level, frm);
	printf("Features:");
	for (i = 0; i < 8; i++)
		printf(" 0x%2.2x", evt->features[i]);
	printf("\n");
}

static inline void evt_le_conn_complete_dump(int level, struct frame *frm)
{
	evt_le_connection_complete *evt = frm->ptr;
	char addr[18];

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d, role %s\n",
					evt->status, btohs(evt->handle),
					evt->role ? "slave" : "master");

	p_indent(level, frm);
	p_ba2str(&evt->peer_bdaddr, addr);
	printf("bdaddr %s (%s)\n", addr, bdaddrtype2str(evt->peer_bdaddr_type));
}

static inline void evt_le_advertising_report_dump(int level, struct frame *frm)
{
	uint8_t num_reports = get_u8(frm);
	const uint8_t RSSI_SIZE = 1;

	while (num_reports--) {
		char addr[18];
		le_advertising_info *info = frm->ptr;
		int offset = 0;

		p_ba2str(&info->bdaddr, addr);

		p_indent(level, frm);
		printf("%s (%d)\n", evttype2str(info->evt_type), info->evt_type);

		p_indent(level, frm);
		printf("bdaddr %s (%s)\n", addr,
					bdaddrtype2str(info->bdaddr_type));

		while (offset < info->length) {
			int eir_data_len = info->data[offset];

			ext_inquiry_data_dump(level, frm, &info->data[offset]);

			offset += eir_data_len + 1;
		}

		frm->ptr += LE_ADVERTISING_INFO_SIZE + info->length;
		frm->len -= LE_ADVERTISING_INFO_SIZE + info->length;

		p_indent(level, frm);
		printf("RSSI: %d\n", ((int8_t *) frm->ptr)[frm->len - 1]);

		frm->ptr += RSSI_SIZE;
		frm->len -= RSSI_SIZE;
	}
}

static inline void evt_le_conn_update_complete_dump(int level,
							struct frame *frm)
{
	evt_le_connection_update_complete *uevt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d\n", uevt->status, btohs(uevt->handle));

	p_indent(level, frm);
	printf("interval %.2fms, latency %.2fms, superv. timeout %.2fms\n",
			btohs(uevt->interval) * 1.25, btohs(uevt->latency) * 1.25,
			btohs(uevt->supervision_timeout) * 10.0);
}

static inline void evt_le_read_remote_used_features_complete_dump(int level, struct frame *frm)
{
	int i;
	evt_le_read_remote_used_features_complete *revt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle %d\n", revt->status, btohs(revt->handle));

	if (revt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(revt->status));
	} else {
		p_indent(level, frm);
		printf("Features:");
		for (i = 0; i < 8; i++)
			printf(" 0x%2.2x", revt->features[i]);
		printf("\n");
	}
}

static inline void le_meta_ev_dump(int level, struct frame *frm)
{
	evt_le_meta_event *mevt = frm->ptr;
	uint8_t subevent;

	subevent = mevt->subevent;

	frm->ptr += EVT_LE_META_EVENT_SIZE;
	frm->len -= EVT_LE_META_EVENT_SIZE;

	p_indent(level, frm);
	printf("%s\n", ev_le_meta_str[subevent]);

	switch (mevt->subevent) {
	case EVT_LE_CONN_COMPLETE:
		evt_le_conn_complete_dump(level + 1, frm);
		break;
	case EVT_LE_ADVERTISING_REPORT:
		evt_le_advertising_report_dump(level + 1, frm);
		break;
	case EVT_LE_CONN_UPDATE_COMPLETE:
		evt_le_conn_update_complete_dump(level + 1, frm);
		break;
	case EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE:
		evt_le_read_remote_used_features_complete_dump(level + 1, frm);
		break;
	default:
		raw_dump(level, frm);
		break;
	}
}

static inline void phys_link_complete_dump(int level, struct frame *frm)
{
	evt_physical_link_complete *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x phy handle 0x%2.2x\n", evt->status, evt->handle);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void disconn_phys_link_complete_dump(int level, struct frame *frm)
{
	evt_disconn_physical_link_complete *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle 0x%2.2x reason 0x%2.2x\n",
				evt->status, evt->handle, evt->reason);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	} else if (evt->reason > 0) {
		p_indent(level, frm);
		printf("Reason: %s\n", status2str(evt->reason));
	}
}

static inline void phys_link_loss_warning_dump(int level, struct frame *frm)
{
	evt_physical_link_loss_warning *evt = frm->ptr;

	p_indent(level, frm);
	printf("phy handle 0x%2.2x reason 0x%2.2x\n", evt->handle, evt->reason);
}

static inline void phys_link_handle_dump(int level, struct frame *frm)
{
	evt_physical_link_recovery *evt = frm->ptr;

	p_indent(level, frm);
	printf("phy handle 0x%2.2x\n", evt->handle);
}

static inline void logical_link_complete_dump(int level, struct frame *frm)
{
	evt_logical_link_complete *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x log handle 0x%4.4x phy handle 0x%2.2x"
							" tx_flow_id %d\n",
			evt->status, btohs(evt->log_handle), evt->handle,
			evt->tx_flow_id);

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void flow_spec_modify_dump(int level, struct frame *frm)
{
	evt_flow_spec_modify_complete *evt = frm->ptr;

	p_indent(level, frm);
	printf("status 0x%2.2x handle 0x%4.4x\n",
					evt->status, btohs(evt->handle));

	if (evt->status > 0) {
		p_indent(level, frm);
		printf("Error: %s\n", status2str(evt->status));
	}
}

static inline void num_completed_blocks_dump(int level, struct frame *frm)
{
	evt_num_completed_blocks *evt = frm->ptr;
	int i;

	p_indent(level, frm);
	printf("Total num blocks %d Num handles %d\n",
			btohs(evt->total_num_blocks), evt->num_handles);

	for (i = 0; i < evt->num_handles; i++) {
		cmplt_handle *h = &evt->handles[i];

		p_indent(level + 1, frm);
		printf("Handle 0x%4.4x: Num complt pkts %d Num complt blks %d\n",
				btohs(h->handle), btohs(h->num_cmplt_pkts),
				btohs(h->num_cmplt_blks));
	}
}

static inline void event_dump(int level, struct frame *frm)
{
	hci_event_hdr *hdr = frm->ptr;
	uint8_t event = hdr->evt;

	if (p_filter(FILT_HCI))
		return;

	if (event <= EVENT_NUM) {
		p_indent(level, frm);
		printf("HCI Event: %s (0x%2.2x) plen %d\n",
					event_str[hdr->evt], hdr->evt, hdr->plen);
	} else if (hdr->evt == EVT_TESTING) {
		p_indent(level, frm);
		printf("HCI Event: Testing (0x%2.2x) plen %d\n", hdr->evt, hdr->plen);
	} else if (hdr->evt == EVT_VENDOR) {
		uint16_t manufacturer;

		if (parser.flags & DUMP_NOVENDOR)
			return;

		p_indent(level, frm);
		printf("HCI Event: Vendor (0x%2.2x) plen %d\n", hdr->evt, hdr->plen);

		manufacturer = get_manufacturer();

		switch (manufacturer) {
		case 0:
		case 37:
		case 48:
			frm->ptr += HCI_EVENT_HDR_SIZE;
			frm->len -= HCI_EVENT_HDR_SIZE;
			ericsson_dump(level + 1, frm);
			return;
		case 10:
			frm->ptr += HCI_EVENT_HDR_SIZE;
			frm->len -= HCI_EVENT_HDR_SIZE;
			csr_dump(level + 1, frm);
			return;
		}
	} else {
		p_indent(level, frm);
		printf("HCI Event: code 0x%2.2x plen %d\n", hdr->evt, hdr->plen);
	}

	frm->ptr += HCI_EVENT_HDR_SIZE;
	frm->len -= HCI_EVENT_HDR_SIZE;

	if (event == EVT_CMD_COMPLETE) {
		evt_cmd_complete *cc = frm->ptr;
		if (cc->opcode == cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION)) {
			read_local_version_rp *rp = frm->ptr + EVT_CMD_COMPLETE_SIZE;
			manufacturer = rp->manufacturer;
		}
	}

	if (event == EVT_DISCONN_COMPLETE) {
		evt_disconn_complete *evt = frm->ptr;
		l2cap_clear(btohs(evt->handle));
	}

	if (!(parser.flags & DUMP_VERBOSE)) {
		raw_dump(level, frm);
		return;
	}

	switch (event) {
	case EVT_LOOPBACK_COMMAND:
		command_dump(level + 1, frm);
		break;
	case EVT_CMD_COMPLETE:
		cmd_complete_dump(level + 1, frm);
		break;
	case EVT_CMD_STATUS:
		cmd_status_dump(level + 1, frm);
		break;
	case EVT_HARDWARE_ERROR:
		hardware_error_dump(level + 1, frm);
		break;
	case EVT_FLUSH_OCCURRED:
	case EVT_QOS_VIOLATION:
		handle_response_dump(level + 1, frm);
		break;
	case EVT_INQUIRY_COMPLETE:
		status_response_dump(level + 1, frm);
		break;
	case EVT_INQUIRY_RESULT:
		inq_result_dump(level + 1, frm);
		break;
	case EVT_CONN_COMPLETE:
		conn_complete_dump(level + 1, frm);
		break;
	case EVT_CONN_REQUEST:
		conn_request_dump(level + 1, frm);
		break;
	case EVT_DISCONN_COMPLETE:
	case EVT_DISCONNECT_LOGICAL_LINK_COMPLETE:
		disconn_complete_dump(level + 1, frm);
		break;
	case EVT_AUTH_COMPLETE:
	case EVT_CHANGE_CONN_LINK_KEY_COMPLETE:
		generic_response_dump(level + 1, frm);
		break;
	case EVT_MASTER_LINK_KEY_COMPLETE:
		master_link_key_complete_dump(level + 1, frm);
		break;
	case EVT_REMOTE_NAME_REQ_COMPLETE:
		remote_name_req_complete_dump(level + 1, frm);
		break;
	case EVT_ENCRYPT_CHANGE:
		encrypt_change_dump(level + 1, frm);
		break;
	case EVT_READ_REMOTE_FEATURES_COMPLETE:
		read_remote_features_complete_dump(level + 1, frm);
		break;
	case EVT_READ_REMOTE_VERSION_COMPLETE:
		read_remote_version_complete_dump(level + 1, frm);
		break;
	case EVT_QOS_SETUP_COMPLETE:
		qos_setup_complete_dump(level + 1, frm);
		break;
	case EVT_ROLE_CHANGE:
		role_change_dump(level + 1, frm);
		break;
	case EVT_NUM_COMP_PKTS:
		num_comp_pkts_dump(level + 1, frm);
		break;
	case EVT_MODE_CHANGE:
		mode_change_dump(level + 1, frm);
		break;
	case EVT_RETURN_LINK_KEYS:
		return_link_keys_dump(level + 1, frm);
		break;
	case EVT_PIN_CODE_REQ:
	case EVT_LINK_KEY_REQ:
	case EVT_IO_CAPABILITY_REQUEST:
	case EVT_USER_PASSKEY_REQUEST:
	case EVT_REMOTE_OOB_DATA_REQUEST:
		pin_code_req_dump(level + 1, frm);
		break;
	case EVT_LINK_KEY_NOTIFY:
		link_key_notify_dump(level + 1, frm);
		break;
	case EVT_DATA_BUFFER_OVERFLOW:
		data_buffer_overflow_dump(level + 1, frm);
		break;
	case EVT_MAX_SLOTS_CHANGE:
		max_slots_change_dump(level + 1, frm);
		break;
	case EVT_READ_CLOCK_OFFSET_COMPLETE:
		read_clock_offset_complete_dump(level + 1, frm);
		break;
	case EVT_CONN_PTYPE_CHANGED:
		conn_ptype_changed_dump(level + 1, frm);
		break;
	case EVT_PSCAN_REP_MODE_CHANGE:
		pscan_rep_mode_change_dump(level + 1, frm);
		break;
	case EVT_FLOW_SPEC_COMPLETE:
		flow_spec_complete_dump(level + 1, frm);
		break;
	case EVT_INQUIRY_RESULT_WITH_RSSI:
		inq_result_with_rssi_dump(level + 1, frm);
		break;
	case EVT_READ_REMOTE_EXT_FEATURES_COMPLETE:
		read_remote_ext_features_complete_dump(level + 1, frm);
		break;
	case EVT_SYNC_CONN_COMPLETE:
		sync_conn_complete_dump(level + 1, frm);
		break;
	case EVT_SYNC_CONN_CHANGED:
		sync_conn_changed_dump(level + 1, frm);
		break;
	case EVT_SNIFF_SUBRATING:
		sniff_subrating_event_dump(level + 1, frm);
		break;
	case EVT_EXTENDED_INQUIRY_RESULT:
		extended_inq_result_dump(level + 1, frm);
		break;
	case EVT_ENCRYPTION_KEY_REFRESH_COMPLETE:
		generic_response_dump(level + 1, frm);
		break;
	case EVT_SIMPLE_PAIRING_COMPLETE:
		bdaddr_response_dump(level + 1, frm);
		break;
	case EVT_LINK_SUPERVISION_TIMEOUT_CHANGED:
		link_supervision_timeout_changed_dump(level + 1, frm);
		break;
	case EVT_ENHANCED_FLUSH_COMPLETE:
		generic_command_dump(level + 1, frm);
		break;
	case EVT_IO_CAPABILITY_RESPONSE:
		io_capability_reply_dump(level + 1, frm);
		break;
	case EVT_USER_CONFIRM_REQUEST:
	case EVT_USER_PASSKEY_NOTIFY:
		user_passkey_notify_dump(level + 1, frm);
		break;
	case EVT_KEYPRESS_NOTIFY:
		keypress_notify_dump(level + 1, frm);
		break;
	case EVT_REMOTE_HOST_FEATURES_NOTIFY:
		remote_host_features_notify_dump(level + 1, frm);
		break;
	case EVT_LE_META_EVENT:
		le_meta_ev_dump(level + 1, frm);
		break;
	case EVT_PHYSICAL_LINK_COMPLETE:
		phys_link_complete_dump(level + 1, frm);
		break;
	case EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE:
		disconn_phys_link_complete_dump(level + 1, frm);
		break;
	case EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING:
		phys_link_loss_warning_dump(level + 1, frm);
		break;
	case EVT_PHYSICAL_LINK_RECOVERY:
	case EVT_CHANNEL_SELECTED:
		phys_link_handle_dump(level + 1, frm);
		break;
	case EVT_LOGICAL_LINK_COMPLETE:
		logical_link_complete_dump(level + 1, frm);
		break;
	case EVT_FLOW_SPEC_MODIFY_COMPLETE:
		flow_spec_modify_dump(level + 1, frm);
		break;
	case EVT_NUMBER_COMPLETED_BLOCKS:
		num_completed_blocks_dump(level + 1, frm);
		break;
	default:
		raw_dump(level, frm);
		break;
	}
}

static inline void acl_dump(int level, struct frame *frm)
{
	hci_acl_hdr *hdr = (void *) frm->ptr;
	uint16_t handle = btohs(hdr->handle);
	uint16_t dlen = btohs(hdr->dlen);
	uint8_t flags = acl_flags(handle);

	if (!p_filter(FILT_HCI)) {
		p_indent(level, frm);
		printf("ACL data: handle %d flags 0x%2.2x dlen %d\n",
			acl_handle(handle), flags, dlen);
		level++;
	}

	frm->ptr += HCI_ACL_HDR_SIZE;
	frm->len -= HCI_ACL_HDR_SIZE;
	frm->flags  = flags;
	frm->handle = acl_handle(handle);

	if (parser.filter & ~FILT_HCI)
		l2cap_dump(level, frm);
	else
		raw_dump(level, frm);
}

static inline void sco_dump(int level, struct frame *frm)
{
	hci_sco_hdr *hdr = (void *) frm->ptr;
	uint16_t handle = btohs(hdr->handle);
	uint8_t flags = acl_flags(handle);
	int len;

	if (frm->audio_fd > fileno(stderr)) {
		len = write(frm->audio_fd, frm->ptr + HCI_SCO_HDR_SIZE, hdr->dlen);
		if (len < 0)
			return;
	}

	if (!p_filter(FILT_SCO)) {
		p_indent(level, frm);
		printf("SCO data: handle %d flags 0x%2.2x dlen %d\n",
				acl_handle(handle), flags, hdr->dlen);
		level++;

		frm->ptr += HCI_SCO_HDR_SIZE;
		frm->len -= HCI_SCO_HDR_SIZE;
		raw_dump(level, frm);
	}
}

static inline void vendor_dump(int level, struct frame *frm)
{
	if (p_filter(FILT_HCI))
		return;

	if (frm->dev_id == HCI_DEV_NONE) {
		uint16_t device = btohs(htons(get_u16(frm)));
		uint16_t proto = btohs(htons(get_u16(frm)));
		uint16_t type = btohs(htons(get_u16(frm)));
		uint16_t plen = btohs(htons(get_u16(frm)));

		p_indent(level, frm);

		printf("System %s: device hci%d proto 0x%2.2x type 0x%2.2x plen %d\n",
			frm->in ? "event" : "command", device, proto, type, plen);

		raw_dump(level, frm);
		return;
	}

	if (parser.flags & DUMP_NOVENDOR)
		return;

	if (get_manufacturer() == 12) {
		bpa_dump(level, frm);
		return;
	}

	p_indent(level, frm);
	printf("Vendor data: len %d\n", frm->len);
	raw_dump(level, frm);
}

void hci_dump(int level, struct frame *frm)
{
	uint8_t type = *(uint8_t *)frm->ptr;

	frm->ptr++; frm->len--;

	switch (type) {
	case HCI_COMMAND_PKT:
		command_dump(level, frm);
		break;

	case HCI_EVENT_PKT:
		event_dump(level, frm);
		break;

	case HCI_ACLDATA_PKT:
		acl_dump(level, frm);
		break;

	case HCI_SCODATA_PKT:
		sco_dump(level, frm);
		break;

	case HCI_VENDOR_PKT:
		vendor_dump(level, frm);
		break;

	default:
		if (p_filter(FILT_HCI))
			break;

		p_indent(level, frm);
		printf("Unknown: type 0x%2.2x len %d\n", type, frm->len);
		raw_dump(level, frm);
		break;
	}
}
