monitor: Add support for handling more management messages
diff --git a/monitor/packet.c b/monitor/packet.c
index e708507..c9d9cae 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -91,8 +91,10 @@
 #define COLOR_CTRL_EVENT		COLOR_MAGENTA_BOLD
 #define COLOR_CTRL_EVENT_UNKNOWN	COLOR_WHITE_BG
 
+#define COLOR_UNKNOWN_OPTIONS_BIT	COLOR_WHITE_BG
 #define COLOR_UNKNOWN_SETTINGS_BIT	COLOR_WHITE_BG
 #define COLOR_UNKNOWN_ADDRESS_TYPE	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_DEVICE_FLAG	COLOR_WHITE_BG
 
 #define COLOR_PHY_PACKET		COLOR_BLUE
 
@@ -9370,6 +9372,34 @@
 static const struct {
 	uint8_t bit;
 	const char *str;
+} mgmt_options_table[] = {
+	{  0, "External configuration"			},
+	{  1, "Bluetooth public address configuration"	},
+	{ }
+};
+
+static void mgmt_print_options(const char *label, uint32_t options)
+{
+	uint32_t mask = options;
+	int i;
+
+	print_field("%s: 0x%8.8x", label, options);
+
+	for (i = 0; mgmt_options_table[i].str; i++) {
+		if (options & (1 << mgmt_options_table[i].bit)) {
+			print_field("  %s", mgmt_options_table[i].str);
+			mask &= ~(1 << mgmt_options_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_OPTIONS_BIT, "  Unknown options"
+							" (0x%8.8x)", mask);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
 } mgmt_settings_table[] = {
 	{  0, "Powered"			},
 	{  1, "Connectable"		},
@@ -9405,8 +9435,8 @@
 	}
 
 	if (mask)
-		print_text(COLOR_UNKNOWN_SETTINGS_BIT, "  Unknown settings "
-							"(0x%8.8x)", mask);
+		print_text(COLOR_UNKNOWN_SETTINGS_BIT, "  Unknown settings"
+							" (0x%8.8x)", mask);
 }
 
 static void mgmt_print_name(const void *data)
@@ -9434,10 +9464,71 @@
 	print_field("%s: %s (0x%2.2x)", label, str, enable);
 }
 
+static void mgmt_print_io_capability(uint8_t capability)
+{
+	const char *str;
+
+	switch (capability) {
+	case 0x00:
+		str = "DisplayOnly";
+		break;
+	case 0x01:
+		str = "DisplayYesNo";
+		break;
+	case 0x02:
+		str = "KeyboardOnly";
+		break;
+	case 0x03:
+		str = "NoInputNoOutput";
+		break;
+	case 0x04:
+		str = "KeyboardDisplay";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Capability: %s (0x%2.2x)", str, capability);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} mgmt_device_flags_table[] = {
+	{  0, "Confirm Name"	},
+	{  1, "Legacy Pairing"	},
+	{  2, "Not Connectable"	},
+	{ }
+};
+
+static void mgmt_print_device_flags(uint32_t flags)
+{
+	uint32_t mask = flags;
+	int i;
+
+	print_field("Flags: 0x%8.8x", flags);
+
+	for (i = 0; mgmt_device_flags_table[i].str; i++) {
+		if (flags & (1 << mgmt_device_flags_table[i].bit)) {
+			print_field("  %s", mgmt_device_flags_table[i].str);
+			mask &= ~(1 << mgmt_device_flags_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_DEVICE_FLAG, "  Unknown device flag"
+							" (0x%8.8x)", mask);
+}
+
 static void mgmt_null_cmd(const void *data, uint16_t size)
 {
 }
 
+static void mgmt_null_rsp(const void *data, uint16_t size)
+{
+}
+
 static void mgmt_read_version_info_rsp(const void *data, uint16_t size)
 {
 	uint8_t version;
@@ -9449,27 +9540,28 @@
 	print_field("Version: %u.%u", version, revision);
 }
 
+static void mgmt_print_commands(const void *data, uint16_t num);
+static void mgmt_print_events(const void *data, uint16_t num);
+
 static void mgmt_read_supported_commands_rsp(const void *data, uint16_t size)
 {
-	uint16_t num_commands;
-	uint16_t num_events;
+	uint16_t num_commands = get_le16(data);
+	uint16_t num_events = get_le16(data + 2);
 
-	num_commands = get_le16(data);
-	num_events = get_le16(data + 2);
+	if (size - 4 != (num_commands * 2) + (num_events *2)) {
+		packet_hexdump(data, size);
+		return;
+	}
 
-	print_field("Commands: %u", num_commands);
-	print_field("Events: %u", num_events);
-
-	packet_hexdump(data + 4, size - 4);
+	mgmt_print_commands(data + 4, num_commands);
+	mgmt_print_events(data + 4 + num_commands * 2, num_events);
 }
 
 static void mgmt_read_index_list_rsp(const void *data, uint16_t size)
 {
-	uint16_t num_controllers;
+	uint16_t num_controllers = get_le16(data);
 	int i;
 
-	num_controllers = get_le16(data);
-
 	print_field("Controllers: %u", num_controllers);
 
 	if (size - 2 != num_controllers * 2) {
@@ -9486,14 +9578,10 @@
 
 static void mgmt_read_controller_info_rsp(const void *data, uint16_t size)
 {
-	uint8_t version;
-	uint16_t manufacturer;
-	uint32_t supported_settings, current_settings;
-
-	version = get_u8(data + 6);
-	manufacturer = get_le16(data + 7);
-	supported_settings = get_le32(data + 9);
-	current_settings = get_le32(data + 13);
+	uint8_t version = get_u8(data + 6);
+	uint16_t manufacturer = get_le16(data + 7);
+	uint32_t supported_settings = get_le32(data + 9);
+	uint32_t current_settings = get_le32(data + 13);
 
 	mgmt_print_address(data, 0x00);
 	mgmt_print_version(version);
@@ -9616,6 +9704,92 @@
 	mgmt_print_name(data);
 }
 
+static void mgmt_disconnect_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_disconnect_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_get_connections_rsp(const void *data, uint16_t size)
+{
+	uint16_t num_connections = get_le16(data);
+	int i;
+
+	print_field("Connections: %u", num_connections);
+
+	if (size - 2 != num_connections * 7) {
+		packet_hexdump(data + 2, size - 2);
+		return;
+	}
+
+	for (i = 0; i < num_connections; i++) {
+		uint16_t address_type = get_le16(data + 2 + (i * 7) + 6);
+
+		mgmt_print_address(data + 2 + (i * 7), address_type);
+	}
+}
+
+static void mgmt_set_io_capability_cmd(const void *data, uint16_t size)
+{
+	uint8_t capability = get_u8(data);
+
+	mgmt_print_io_capability(capability);
+}
+
+static void mgmt_pair_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t capability = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_io_capability(capability);
+}
+
+static void mgmt_pair_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_cancel_pair_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_cancel_pair_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
+static void mgmt_unpair_device_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t disconnect = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_enable("Disconnect", disconnect);
+}
+
+static void mgmt_unpair_device_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
 static void mgmt_start_discovery_cmd(const void *data, uint16_t size)
 {
 	uint8_t type = get_u8(data);
@@ -9644,6 +9818,36 @@
 	mgmt_print_address_type(type);
 }
 
+static void mgmt_confirm_name_cmd(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t name_known = get_u8(data + 7);
+	const char *str;
+
+	mgmt_print_address(data, address_type);
+
+	switch (name_known) {
+	case 0x00:
+		str = "No";
+		break;
+	case 0x01:
+		str = "Yes";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Name known: %s (0x%2.2x)", str, name_known);
+}
+
+static void mgmt_confirm_name_rsp(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+
+	mgmt_print_address(data, address_type);
+}
+
 static void mgmt_set_advertising_cmd(const void *data, uint16_t size)
 {
 	uint8_t enable = get_u8(data);
@@ -9745,11 +9949,9 @@
 
 static void mgmt_read_unconf_index_list_rsp(const void *data, uint16_t size)
 {
-	uint16_t num_controllers;
+	uint16_t num_controllers = get_le16(data);
 	int i;
 
-	num_controllers = get_le16(data);
-
 	print_field("Controllers: %u", num_controllers);
 
 	if (size - 2 != num_controllers * 2) {
@@ -9764,13 +9966,22 @@
 	}
 }
 
+static void mgmt_read_controller_conf_info_rsp(const void *data, uint16_t size)
+{
+	uint16_t manufacturer = get_le16(data);
+	uint32_t supported_options = get_le32(data + 2);
+	uint32_t missing_options = get_le32(data + 6);
+
+	mgmt_print_manufacturer(manufacturer);
+	mgmt_print_options("Supported options", supported_options);
+	mgmt_print_options("Missing options", missing_options);
+}
+
 static void mgmt_read_ext_index_list_rsp(const void *data, uint16_t size)
 {
-	uint16_t num_controllers;
+	uint16_t num_controllers = get_le16(data);
 	int i;
 
-	num_controllers = get_le16(data);
-
 	print_field("Controllers: %u", num_controllers);
 
 	if (size - 2 != num_controllers * 4) {
@@ -9870,15 +10081,26 @@
 	{ 0x0011, "Remove UUID" },
 	{ 0x0012, "Load Link Keys" },
 	{ 0x0013, "Load Long Term Keys" },
-	{ 0x0014, "Disconnect" },
+	{ 0x0014, "Disconnect",
+				mgmt_disconnect_cmd, 7, true,
+				mgmt_disconnect_rsp, 7, true },
 	{ 0x0015, "Get Connections",
-				mgmt_null_cmd, 0, true },
+				mgmt_null_cmd, 0, true,
+				mgmt_get_connections_rsp, 2, false },
 	{ 0x0016, "PIN Code Reply" },
 	{ 0x0017, "PIN Code Negative Reply" },
-	{ 0x0018, "Set IO Capability" },
-	{ 0x0019, "Pair Device" },
-	{ 0x001a, "Cancel Pair Device" },
-	{ 0x001b, "Unpair Device" },
+	{ 0x0018, "Set IO Capability",
+				mgmt_set_io_capability_cmd, 1, true,
+				mgmt_null_rsp, 0, true },
+	{ 0x0019, "Pair Device",
+				mgmt_pair_device_cmd, 8, true,
+				mgmt_pair_device_rsp, 7, true },
+	{ 0x001a, "Cancel Pair Device",
+				mgmt_cancel_pair_device_cmd, 7, true,
+				mgmt_cancel_pair_device_rsp, 7, true },
+	{ 0x001b, "Unpair Device",
+				mgmt_unpair_device_cmd, 8, true,
+				mgmt_unpair_device_rsp, 7, true },
 	{ 0x001c, "User Confirmation Reply" },
 	{ 0x001d, "User Confirmation Negative Reply" },
 	{ 0x001e, "User Passkey Reply" },
@@ -9893,7 +10115,9 @@
 	{ 0x0024, "Stop Discovery",
 				mgmt_stop_discovery_cmd, 1, true,
 				mgmt_stop_discovery_rsp, 1, true },
-	{ 0x0025, "Confirm Name" },
+	{ 0x0025, "Confirm Name",
+				mgmt_confirm_name_cmd, 8, true,
+				mgmt_confirm_name_rsp, 7, true },
 	{ 0x0026, "Block Device" },
 	{ 0x0027, "Unblock Device" },
 	{ 0x0028, "Set Device ID" },
@@ -9924,7 +10148,8 @@
 				mgmt_null_cmd, 0, true,
 				mgmt_read_unconf_index_list_rsp, 2, false },
 	{ 0x0037, "Read Controller Configuration Information",
-				mgmt_null_cmd, 0, true },
+				mgmt_null_cmd, 0, true,
+				mgmt_read_controller_conf_info_rsp, 10, true },
 	{ 0x0038, "Set External Configuration" },
 	{ 0x0039, "Set Public Address" },
 	{ 0x003a, "Start Service Discovery" },
@@ -9985,7 +10210,7 @@
 
 	mgmt_print_status(status);
 
-	if (status || !mgmt_data || !mgmt_data->rsp_func) {
+	if (!mgmt_data || !mgmt_data->rsp_func) {
 		packet_hexdump(data, size);
 		return;
 	}
@@ -10063,6 +10288,73 @@
 	mgmt_print_name(data);
 }
 
+static void mgmt_device_connected_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint32_t flags = get_le32(data + 7);
+	uint16_t data_len = get_le16(data + 11);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_device_flags(flags);
+	print_field("Data length: %u", data_len);
+	print_eir(data + 13, size - 13, false);
+}
+
+static void mgmt_device_disconnected_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t reason = get_u8(data + 7);
+	const char *str;
+
+	mgmt_print_address(data, address_type);
+
+	switch (reason) {
+	case 0x00:
+		str = "Unspecified";
+		break;
+	case 0x01:
+		str = "Connection timeout";
+		break;
+	case 0x02:
+		str = "Connection terminated by local host";
+		break;
+	case 0x03:
+		str = "Connection terminated by remote host";
+		break;
+	case 0x04:
+		str = "Connection terminated due to authentication failure";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reason: %s (0x%2.2x)", str, reason);
+}
+
+static void mgmt_connect_failed_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	uint8_t status = get_u8(data + 7);
+
+	mgmt_print_address(data, address_type);
+	mgmt_print_status(status);
+}
+
+static void mgmt_device_found_evt(const void *data, uint16_t size)
+{
+	uint8_t address_type = get_u8(data + 6);
+	int8_t rssi = get_s8(data + 7);
+	uint32_t flags = get_le32(data + 8);
+	uint16_t data_len = get_le16(data + 12);
+
+	mgmt_print_address(data, address_type);
+	print_rssi(rssi);
+	mgmt_print_device_flags(flags);
+	print_field("Data length: %u", data_len);
+	print_eir(data + 14, size - 14, false);
+}
+
 static void mgmt_discovering_evt(const void *data, uint16_t size)
 {
 	uint8_t type = get_u8(data);
@@ -10091,14 +10383,18 @@
 			mgmt_local_name_changed_evt, 260, true },
 	{ 0x0009, "New Link Key" },
 	{ 0x000a, "New Long Term Key" },
-	{ 0x000b, "Device Connected" },
-	{ 0x000c, "Device Disconnected" },
-	{ 0x000d, "Connect Failed" },
+	{ 0x000b, "Device Connected",
+			mgmt_device_connected_evt, 13, false },
+	{ 0x000c, "Device Disconnected",
+			mgmt_device_disconnected_evt, 8, true },
+	{ 0x000d, "Connect Failed",
+			mgmt_connect_failed_evt, 8, true },
 	{ 0x000e, "PIN Code Request" },
 	{ 0x000f, "User Confirmation Request" },
 	{ 0x0010, "User Passkey Request" },
 	{ 0x0011, "Authentication Failed" },
-	{ 0x0012, "Device Found" },
+	{ 0x0012, "Device Found",
+			mgmt_device_found_evt, 14, false },
 	{ 0x0013, "Discovering",
 			mgmt_discovering_evt, 2, true },
 	{ 0x0014, "Device Blocked" },
@@ -10123,6 +10419,50 @@
 	{ }
 };
 
+static void mgmt_print_commands(const void *data, uint16_t num)
+{
+	int i;
+
+	print_field("Commands: %u", num);
+
+	for (i = 0; i < num; i++) {
+		uint16_t opcode = get_le16(data + (i * 2));
+		const char *str = NULL;
+		int n;
+
+		for (n = 0; mgmt_command_table[n].str; n++) {
+			if (mgmt_command_table[n].opcode == opcode) {
+				str = mgmt_command_table[n].str;
+				break;
+			}
+		}
+
+		print_field("  %s (0x%4.4x)", str ?: "Reserved", opcode);
+	}
+}
+
+static void mgmt_print_events(const void *data, uint16_t num)
+{
+	int i;
+
+	print_field("Events: %u", num);
+
+	for (i = 0; i < num; i++) {
+		uint16_t opcode = get_le16(data + (i * 2));
+		const char *str = NULL;
+		int n;
+
+		for (n = 0; mgmt_event_table[n].str; n++) {
+			if (mgmt_event_table[n].opcode == opcode) {
+				str = mgmt_event_table[n].str;
+				break;
+			}
+		}
+
+		print_field("  %s (0x%4.4x)", str ?: "Reserved", opcode);
+	}
+}
+
 void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index,
 					const void *data, uint16_t size)
 {