mgmt: Implement set_pairable

This patch implements support for the set_pairable managment command.
Due to the async nature of it a new btd_adapter_pairable_changed
function is added to the core daemon.
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index ba42950..e785a5e 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -107,6 +107,15 @@
 				Connectable (1 Octet)
 
 
+Set Pairable Command
+====================
+
+	Command Code:		0x0008
+	Command Parameters:	Controller_Index (2 Octets)
+				Pairable (1 Octet)
+	Return Paramters:	Controller_Index (2 Octets)
+				Pairable (1 Octet)
+
 
 Read Tracing Buffer Size Command
 ================================
@@ -216,3 +225,10 @@
 Event Code		0x0008
 Event Parameters	Controller_Index (2 Octets)
 			Connectable (1 Octet)
+
+Controller Pairable Event
+=========================
+
+Event Code		0x0009
+Event Parameters	Controller_Index (2 Octets)
+			Pairable (1 Octet)
diff --git a/lib/mgmt.h b/lib/mgmt.h
index 3657f71..1c30949 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -79,6 +79,8 @@
 
 #define MGMT_OP_SET_CONNECTABLE		0x0007
 
+#define MGMT_OP_SET_PAIRABLE		0x0008
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	uint16_t opcode;
@@ -112,3 +114,5 @@
 #define MGMT_EV_DISCOVERABLE		0x0007
 
 #define MGMT_EV_CONNECTABLE		0x0008
+
+#define MGMT_EV_PAIRABLE		0x0009
diff --git a/plugins/hciops.c b/plugins/hciops.c
index 9861d60..ad83eb2 100644
--- a/plugins/hciops.c
+++ b/plugins/hciops.c
@@ -310,8 +310,15 @@
 
 static int hciops_set_pairable(int index, gboolean pairable)
 {
+	struct btd_adapter *adapter;
+
 	DBG("hci%d pairable %d", index, pairable);
-	return -ENOSYS;
+
+	adapter = manager_find_adapter(&devs[index].bdaddr);
+	if (adapter)
+		btd_adapter_pairable_changed(adapter, pairable);
+
+	return 0;
 }
 
 static int hciops_power_off(int index)
diff --git a/plugins/mgmtops.c b/plugins/mgmtops.c
index 32d88d3..d4378d8 100644
--- a/plugins/mgmtops.c
+++ b/plugins/mgmtops.c
@@ -206,7 +206,7 @@
 static int mgmt_set_pairable(int index, gboolean pairable)
 {
 	DBG("index %d pairable %d", index, pairable);
-	return -ENOSYS;
+	return mgmt_set_mode(index, MGMT_OP_SET_PAIRABLE, pairable);
 }
 
 static int mgmt_update_powered(int index, uint8_t powered)
@@ -261,7 +261,8 @@
 		adapter_mode_changed(adapter, mode);
 	}
 
-	mgmt_set_pairable(index, pairable);
+	if (info->pairable != pairable)
+		mgmt_set_pairable(index, pairable);
 
 	return 0;
 }
@@ -365,6 +366,38 @@
 	adapter_mode_changed(adapter, mode);
 }
 
+static void mgmt_pairable(int sk, void *buf, size_t len)
+{
+	struct mgmt_mode *ev = buf;
+	struct controller_info *info;
+	struct btd_adapter *adapter;
+	uint16_t index;
+
+	if (len < sizeof(*ev)) {
+		error("Too small pairable event");
+		return;
+	}
+
+	index = btohs(bt_get_unaligned(&ev->index));
+
+	DBG("Controller %u pairable %u", index, ev->val);
+
+	if (index > max_index) {
+		error("Unexpected index %u in pairable event", index);
+		return;
+	}
+
+	info = &controllers[index];
+
+	info->pairable = ev->val ? TRUE : FALSE;
+
+	adapter = manager_find_adapter(&info->bdaddr);
+	if (!adapter)
+		return;
+
+	btd_adapter_pairable_changed(adapter, info->pairable);
+}
+
 static void read_index_list_complete(int sk, void *buf, size_t len)
 {
 	struct mgmt_rp_read_index_list *rp = buf;
@@ -549,6 +582,38 @@
 		adapter_mode_changed(adapter, rp->val ? SCAN_PAGE : 0);
 }
 
+static void set_pairable_complete(int sk, void *buf, size_t len)
+{
+	struct mgmt_mode *rp = buf;
+	struct controller_info *info;
+	struct btd_adapter *adapter;
+	uint16_t index;
+
+	if (len < sizeof(*rp)) {
+		error("Too small set pairable complete event");
+		return;
+	}
+
+	index = btohs(bt_get_unaligned(&rp->index));
+
+	DBG("hci%d pairable %u", index, rp->val);
+
+	if (index > max_index) {
+		error("Unexpected index %u in pairable complete", index);
+		return;
+	}
+
+	info = &controllers[index];
+
+	info->pairable = rp->val ? TRUE : FALSE;
+
+	adapter = manager_find_adapter(&info->bdaddr);
+	if (!adapter)
+		return;
+
+	btd_adapter_pairable_changed(adapter, info->pairable);
+}
+
 static void mgmt_cmd_complete(int sk, void *buf, size_t len)
 {
 	struct mgmt_ev_cmd_complete *ev = buf;
@@ -582,6 +647,9 @@
 	case MGMT_OP_SET_CONNECTABLE:
 		set_connectable_complete(sk, ev->data, len - sizeof(*ev));
 		break;
+	case MGMT_OP_SET_PAIRABLE:
+		set_pairable_complete(sk, ev->data, len - sizeof(*ev));
+		break;
 	default:
 		error("Unknown command complete for opcode %u", opcode);
 		break;
@@ -685,6 +753,9 @@
 	case MGMT_EV_CONNECTABLE:
 		mgmt_connectable(sk, buf + MGMT_HDR_SIZE, len);
 		break;
+	case MGMT_EV_PAIRABLE:
+		mgmt_pairable(sk, buf + MGMT_HDR_SIZE, len);
+		break;
 	default:
 		error("Unknown Management opcode %u", opcode);
 		break;
diff --git a/src/adapter.c b/src/adapter.c
index e6a2081..87a8beb 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -546,6 +546,22 @@
 	return NULL;
 }
 
+void btd_adapter_pairable_changed(struct btd_adapter *adapter,
+							gboolean pairable)
+{
+	adapter->pairable = pairable;
+
+	write_device_pairable(&adapter->bdaddr, pairable);
+
+	emit_property_changed(connection, adapter->path,
+				ADAPTER_INTERFACE, "Pairable",
+				DBUS_TYPE_BOOLEAN, &pairable);
+
+	if (pairable && adapter->pairable_timeout)
+		adapter_set_pairable_timeout(adapter,
+						adapter->pairable_timeout);
+}
+
 static DBusMessage *set_pairable(DBusConnection *conn, DBusMessage *msg,
 				gboolean pairable, void *data)
 {
@@ -566,20 +582,8 @@
 		return btd_error_failed(msg, strerror(-err));
 
 store:
-	adapter->pairable = pairable;
-
 	adapter_ops->set_pairable(adapter->dev_id, pairable);
 
-	write_device_pairable(&adapter->bdaddr, pairable);
-
-	emit_property_changed(connection, adapter->path,
-				ADAPTER_INTERFACE, "Pairable",
-				DBUS_TYPE_BOOLEAN, &pairable);
-
-	if (pairable && adapter->pairable_timeout)
-		adapter_set_pairable_timeout(adapter,
-						adapter->pairable_timeout);
-
 done:
 	return msg ? dbus_message_new_method_return(msg) : NULL;
 }
diff --git a/src/adapter.h b/src/adapter.h
index 4599ea7..250c65e 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -151,6 +151,8 @@
 void adapter_service_remove(struct btd_adapter *adapter, void *rec);
 void btd_adapter_class_changed(struct btd_adapter *adapter,
 							uint32_t new_class);
+void btd_adapter_pairable_changed(struct btd_adapter *adapter,
+							gboolean pairable);
 
 struct agent *adapter_get_agent(struct btd_adapter *adapter);
 void adapter_add_connection(struct btd_adapter *adapter,