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;