mgmt: Implement set_connectable
diff --git a/lib/mgmt.h b/lib/mgmt.h
index cbc5302..1af8279 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -56,6 +56,7 @@
 	uint16_t index;
 	uint8_t type;
 	uint8_t powered;
+	uint8_t connectable;
 	uint8_t discoverable;
 	uint8_t pairable;
 	uint8_t sec_mode;
@@ -87,6 +88,16 @@
 	uint8_t discoverable;
 } __packed;
 
+#define MGMT_OP_SET_CONNECTABLE		0x0007
+struct mgmt_cp_set_connectable {
+	uint16_t index;
+	uint8_t connectable;
+} __packed;
+struct mgmt_rp_set_connectable {
+	uint16_t index;
+	uint8_t connectable;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	uint16_t opcode;
@@ -126,3 +137,9 @@
 	uint16_t index;
 	uint8_t discoverable;
 } __packed;
+
+#define MGMT_EV_CONNECTABLE		0x0008
+struct mgmt_ev_connectable {
+	uint16_t index;
+	uint8_t connectable;
+} __packed;
diff --git a/plugins/mgmtops.c b/plugins/mgmtops.c
index fbd1ac1..441ed44 100644
--- a/plugins/mgmtops.c
+++ b/plugins/mgmtops.c
@@ -60,6 +60,7 @@
 	uint8_t hci_ver;
 	uint16_t hci_rev;
 	gboolean enabled;
+	gboolean connectable;
 	gboolean discoverable;
 	gboolean pairable;
 	uint8_t sec_mode;
@@ -171,6 +172,27 @@
 	remove_controller(index);
 }
 
+static int mgmt_set_connectable(int index, gboolean connectable)
+{
+	char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_connectable)];
+	struct mgmt_hdr *hdr = (void *) buf;
+	struct mgmt_cp_set_connectable *cp = (void *) &buf[sizeof(*hdr)];
+
+	DBG("index %d connectable %d", index, connectable);
+
+	memset(buf, 0, sizeof(buf));
+	hdr->opcode = MGMT_OP_SET_CONNECTABLE;
+	hdr->len = htobs(sizeof(*cp));
+
+	cp->index = htobs(index);
+	cp->connectable = connectable;
+
+	if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+		return -errno;
+
+	return 0;
+}
+
 static int mgmt_set_discoverable(int index, gboolean discoverable)
 {
 	char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_discoverable)];
@@ -221,6 +243,7 @@
 	}
 
 	if (!powered) {
+		info->connectable = FALSE;
 		info->pairable = FALSE;
 		info->discoverable = FALSE;
 
@@ -234,7 +257,20 @@
 
 	discoverable = (on_mode == MODE_DISCOVERABLE);
 
-	mgmt_set_discoverable(index, discoverable);
+	if (on_mode == MODE_DISCOVERABLE && !info->discoverable)
+		mgmt_set_discoverable(index, TRUE);
+	else if (on_mode == MODE_CONNECTABLE && !info->connectable)
+		mgmt_set_connectable(index, TRUE);
+	else {
+		uint8_t mode = 0;
+
+		if (info->connectable)
+			mode |= SCAN_PAGE;
+		if (info->discoverable)
+			mode |= SCAN_INQUIRY;
+
+		adapter_mode_changed(adapter, mode);
+	}
 
 	mgmt_set_pairable(index, pairable);
 
@@ -288,6 +324,47 @@
 		adapter_mode_changed(adapter, ev->discoverable ? 0x03 : 0x02);
 }
 
+static void mgmt_connectable(int sk, void *buf, size_t len)
+{
+	struct mgmt_ev_connectable *ev = buf;
+	struct controller_info *info;
+	struct btd_adapter *adapter;
+	uint16_t index;
+	uint8_t mode;
+
+	if (len < sizeof(*ev)) {
+		error("Too small connectable event");
+		return;
+	}
+
+	index = btohs(bt_get_unaligned(&ev->index));
+
+	DBG("Controller %u connectable %u", index, ev->connectable);
+
+	if (index > max_index) {
+		error("Unexpected index %u in connectable event", index);
+		return;
+	}
+
+	info = &controllers[index];
+
+	info->connectable = ev->connectable ? TRUE : FALSE;
+
+	adapter = manager_find_adapter(&info->bdaddr);
+	if (!adapter)
+		return;
+
+	if (info->discoverable)
+		mode = SCAN_INQUIRY;
+	else
+		mode = 0;
+
+	if (info->connectable)
+		mode |= SCAN_PAGE;
+
+	adapter_mode_changed(adapter, mode);
+}
+
 static void read_index_list_complete(int sk, void *buf, size_t len)
 {
 	struct mgmt_rp_read_index_list *rp = buf;
@@ -360,6 +437,7 @@
 	info = &controllers[index];
 	info->type = rp->type;
 	info->enabled = rp->powered;
+	info->connectable = rp->connectable;
 	info->discoverable = rp->discoverable;
 	info->pairable = rp->pairable;
 	info->sec_mode = rp->sec_mode;
@@ -392,7 +470,10 @@
 		return;
 	}
 
-	mgmt_update_mode(index, TRUE);
+	if (info->enabled)
+		mgmt_update_mode(index, TRUE);
+	else
+		mgmt_set_powered(index, TRUE);
 
 	btd_adapter_unref(adapter);
 }
@@ -444,6 +525,36 @@
 		adapter_mode_changed(adapter, rp->discoverable ? 0x03 : 0x02);
 }
 
+static void set_connectable_complete(int sk, void *buf, size_t len)
+{
+	struct mgmt_rp_set_connectable *rp = buf;
+	struct controller_info *info;
+	struct btd_adapter *adapter;
+	uint16_t index;
+
+	if (len < sizeof(*rp)) {
+		error("Too small set connectable complete event");
+		return;
+	}
+
+	index = btohs(bt_get_unaligned(&rp->index));
+
+	DBG("hci%d connectable %u", index, rp->connectable);
+
+	if (index > max_index) {
+		error("Unexpected index %u in connectable complete", index);
+		return;
+	}
+
+	info = &controllers[index];
+
+	info->connectable = rp->connectable ? TRUE : FALSE;
+
+	adapter = manager_find_adapter(&info->bdaddr);
+	if (adapter)
+		adapter_mode_changed(adapter, rp->connectable ? SCAN_PAGE : 0);
+}
+
 static void mgmt_cmd_complete(int sk, void *buf, size_t len)
 {
 	struct mgmt_ev_cmd_complete *ev = buf;
@@ -474,6 +585,9 @@
 	case MGMT_OP_SET_DISCOVERABLE:
 		set_discoverable_complete(sk, ev->data, len - sizeof(*ev));
 		break;
+	case MGMT_OP_SET_CONNECTABLE:
+		set_connectable_complete(sk, ev->data, len - sizeof(*ev));
+		break;
 	default:
 		error("Unknown command complete for opcode %u", opcode);
 		break;
@@ -574,6 +688,9 @@
 	case MGMT_EV_DISCOVERABLE:
 		mgmt_discoverable(sk, buf + MGMT_HDR_SIZE, len);
 		break;
+	case MGMT_EV_CONNECTABLE:
+		mgmt_connectable(sk, buf + MGMT_HDR_SIZE, len);
+		break;
 	default:
 		error("Unknown Management opcode %u", opcode);
 		break;