Bluetooth: Fix Limited Discoverability for MGMTOPS
CRs-fixed: 309551
Change-Id: I42c46c62dcb4b227d9ac48146ee61cb52063a6ae
Signed-off-by: Brian Gix <bgix@codeaurora.org>
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index aa6aeb9..b67803a 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -708,6 +708,12 @@
__le16 sco_max_pkt;
} __packed;
+#define HCI_OP_WRITE_CURRENT_IAC_LAP 0x0c3a
+struct hci_cp_write_current_iac_lap {
+ __u8 num_current_iac;
+ __u8 lap[6];
+} __packed;
+
#define HCI_OP_WRITE_INQUIRY_MODE 0x0c45
#define HCI_MAX_EIR_LENGTH 240
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 5e855dbf..208c157 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -90,6 +90,8 @@
__u8 major;
__u8 minor;
} __packed;
+#define MGMT_MAJOR_CLASS_MASK 0x1F
+#define MGMT_MAJOR_CLASS_LIMITED 0x20
#define MGMT_OP_SET_SERVICE_CACHE 0x000C
struct mgmt_cp_set_service_cache {
@@ -214,6 +216,8 @@
bdaddr_t bdaddr;
} __packed;
+#define MGMT_OP_SET_LIMIT_DISCOVERABLE 0x001F
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a328db9..ea72708 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -409,6 +409,124 @@
return err;
}
+static u8 get_service_classes(struct hci_dev *hdev)
+{
+ struct list_head *p;
+ u8 val = 0;
+
+ list_for_each(p, &hdev->uuids) {
+ struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
+
+ val |= uuid->svc_hint;
+ }
+
+ return val;
+}
+
+static int update_class(struct hci_dev *hdev)
+{
+ u8 cod[3];
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ return 0;
+
+ cod[0] = hdev->minor_class;
+ cod[1] = hdev->major_class;
+ cod[2] = get_service_classes(hdev);
+
+ if (memcmp(cod, hdev->dev_class, 3) == 0)
+ return 0;
+
+ return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+}
+
+static int set_limited_discoverable(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct mgmt_mode *cp;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ struct hci_cp_write_current_iac_lap dcp;
+ int update_cod;
+ int err = 0;
+ /* General Inquiry LAP: 0x9E8B33, Limited Inquiry LAP: 0x9E8B00 */
+ u8 lap[] = { 0x33, 0x8b, 0x9e, 0x00, 0x8b, 0x9e };
+
+ cp = (void *) data;
+
+ BT_DBG("hci%u discoverable: %d", index, cp->val);
+
+ if (!cp || len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ ENODEV);
+
+ hci_dev_lock(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ ENETDOWN);
+ goto failed;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_LIMIT_DISCOVERABLE, index)) {
+ err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ EBUSY);
+ goto failed;
+ }
+
+ if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
+ test_bit(HCI_PSCAN, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ EALREADY);
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_LIMIT_DISCOVERABLE, index, data,
+ len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ memset(&dcp, 0, sizeof(dcp));
+ dcp.num_current_iac = cp->val ? 2 : 1;
+ memcpy(&dcp.lap, lap, dcp.num_current_iac * 3);
+ update_cod = 1;
+
+ if (cp->val) {
+ if (hdev->major_class & MGMT_MAJOR_CLASS_LIMITED)
+ update_cod = 0;
+ hdev->major_class |= MGMT_MAJOR_CLASS_LIMITED;
+ } else {
+ if (!(hdev->major_class & MGMT_MAJOR_CLASS_LIMITED))
+ update_cod = 0;
+ hdev->major_class &= ~MGMT_MAJOR_CLASS_LIMITED;
+ }
+
+ if (update_cod)
+ err = update_class(hdev);
+
+ if (err >= 0)
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_CURRENT_IAC_LAP,
+ sizeof(dcp), &dcp);
+
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+failed:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
@@ -751,39 +869,6 @@
return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
}
-static u8 get_service_classes(struct hci_dev *hdev)
-{
- struct list_head *p;
- u8 val = 0;
-
- list_for_each(p, &hdev->uuids) {
- struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
-
- val |= uuid->svc_hint;
- }
-
- return val;
-}
-
-static int update_class(struct hci_dev *hdev)
-{
- u8 cod[3];
-
- BT_DBG("%s", hdev->name);
-
- if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
- return 0;
-
- cod[0] = hdev->minor_class;
- cod[1] = hdev->major_class;
- cod[2] = get_service_classes(hdev);
-
- if (memcmp(cod, hdev->dev_class, 3) == 0)
- return 0;
-
- return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
-}
-
static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
{
struct mgmt_cp_add_uuid *cp;
@@ -912,7 +997,8 @@
hci_dev_lock(hdev);
- hdev->major_class = cp->major;
+ hdev->major_class &= ~MGMT_MAJOR_CLASS_MASK;
+ hdev->major_class |= cp->major & MGMT_MAJOR_CLASS_MASK;
hdev->minor_class = cp->minor;
err = update_class(hdev);
@@ -2111,6 +2197,10 @@
case MGMT_OP_SET_DISCOVERABLE:
err = set_discoverable(sk, index, buf + sizeof(*hdr), len);
break;
+ case MGMT_OP_SET_LIMIT_DISCOVERABLE:
+ err = set_limited_discoverable(sk, index, buf + sizeof(*hdr),
+ len);
+ break;
case MGMT_OP_SET_CONNECTABLE:
err = set_connectable(sk, index, buf + sizeof(*hdr), len);
break;