bluetooth: LE conn whitelist implementation
Add support to create connection to LE devices using
whitelist. With this, the Controller can try to connect
to multiple devices at the same time. The following
interfaces are added.
1. Add/Remove device from whitelist
2. Clear all the devices from whitelist
3. Create Connection to devices from whitelist
4. Cancel create connection to whitelist devices
CRs-fixed: 388980
Change-Id: I3900c71255e754f80bb2873ae19a41b94cca76c3
Signed-off-by: Sunny Kapdi <sunnyk@codeaurora.org>
(cherry picked from commit 93bef895b01b79f49af60ba1394c9c3f6e563212)
(cherry picked from commit 377ee2bf1fc37bcbeae872661646bdd6a5f8da31)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index c72b6ed..d7deaaf 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -229,6 +229,8 @@
memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
+ rp.le_white_list_size = hdev->le_white_list_size;
+
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
@@ -1450,6 +1452,185 @@
return err;
}
+static int le_add_dev_white_list(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_le_add_dev_white_list *cp;
+ int err = 0;
+
+ BT_DBG("");
+
+ cp = (void *) data;
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_LE_ADD_DEV_WHITE_LIST,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_LE_ADD_DEV_WHITE_LIST,
+ ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_LE_ADD_DEV_WHITE_LIST,
+ ENETDOWN);
+ goto failed;
+ }
+
+ hci_le_add_dev_white_list(hdev, &cp->bdaddr);
+
+failed:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int le_remove_dev_white_list(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_le_remove_dev_white_list *cp;
+ int err = 0;
+
+ BT_DBG("");
+
+ cp = (void *) data;
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_LE_REMOVE_DEV_WHITE_LIST,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_LE_REMOVE_DEV_WHITE_LIST,
+ ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_LE_REMOVE_DEV_WHITE_LIST,
+ ENETDOWN);
+ goto failed;
+ }
+
+ hci_le_remove_dev_white_list(hdev, &cp->bdaddr);
+
+failed:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int le_create_conn_white_list(struct sock *sk, u16 index)
+{
+ struct hci_dev *hdev;
+ struct hci_conn *conn;
+ u8 sec_level, auth_type;
+ struct pending_cmd *cmd;
+ bdaddr_t bdaddr;
+ int err = 0;
+
+ BT_DBG("");
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_LE_CREATE_CONN_WHITE_LIST,
+ ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_LE_CREATE_CONN_WHITE_LIST,
+ ENETDOWN);
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_LE_CREATE_CONN_WHITE_LIST, index,
+ NULL, 0);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ sec_level = BT_SECURITY_MEDIUM;
+ auth_type = HCI_AT_GENERAL_BONDING;
+ memset(&bdaddr, 0, sizeof(bdaddr));
+ conn = hci_le_connect(hdev, 0, BDADDR_ANY, sec_level, auth_type, NULL);
+ if (IS_ERR(conn)) {
+ err = PTR_ERR(conn);
+ mgmt_pending_remove(cmd);
+ }
+
+failed:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int le_cancel_create_conn_white_list(struct sock *sk, u16 index)
+{
+ struct hci_dev *hdev;
+ int err = 0;
+
+ BT_DBG("");
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index,
+ MGMT_OP_LE_CANCEL_CREATE_CONN_WHITE_LIST, ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index,
+ MGMT_OP_LE_CANCEL_CREATE_CONN_WHITE_LIST, ENETDOWN);
+ goto failed;
+ }
+
+ hci_le_cancel_create_connect(hdev, BDADDR_ANY);
+
+failed:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int le_clear_white_list(struct sock *sk, u16 index)
+{
+ struct hci_dev *hdev;
+ int err;
+
+ BT_DBG("");
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index,
+ MGMT_OP_LE_CLEAR_WHITE_LIST, ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index,
+ MGMT_OP_LE_CLEAR_WHITE_LIST, ENETDOWN);
+ goto failed;
+ }
+
+ err = hci_send_cmd(hdev, HCI_OP_LE_CLEAR_WHITE_LIST, 0, NULL);
+
+failed:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
@@ -2466,7 +2647,23 @@
case MGMT_OP_ENCRYPT_LINK:
err = encrypt_link(sk, index, buf + sizeof(*hdr), len);
break;
-
+ case MGMT_OP_LE_ADD_DEV_WHITE_LIST:
+ err = le_add_dev_white_list(sk, index, buf + sizeof(*hdr),
+ len);
+ break;
+ case MGMT_OP_LE_REMOVE_DEV_WHITE_LIST:
+ err = le_remove_dev_white_list(sk, index, buf + sizeof(*hdr),
+ len);
+ break;
+ case MGMT_OP_LE_CLEAR_WHITE_LIST:
+ err = le_clear_white_list(sk, index);
+ break;
+ case MGMT_OP_LE_CREATE_CONN_WHITE_LIST:
+ err = le_create_conn_white_list(sk, index);
+ break;
+ case MGMT_OP_LE_CANCEL_CREATE_CONN_WHITE_LIST:
+ err = le_cancel_create_conn_white_list(sk, index);
+ break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode, 0x01);
@@ -2626,10 +2823,25 @@
int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 le)
{
struct mgmt_ev_connected ev;
+ struct pending_cmd *cmd;
+ struct hci_dev *hdev;
+
+ BT_DBG("hci%u", index);
+
+ hdev = hci_dev_get(index);
+
+ if (!hdev)
+ return -ENODEV;
bacpy(&ev.bdaddr, bdaddr);
ev.le = le;
+ cmd = mgmt_pending_find(MGMT_OP_LE_CREATE_CONN_WHITE_LIST, index);
+ if (cmd) {
+ BT_ERR("mgmt_connected remove mgmt pending white_list");
+ mgmt_pending_remove(cmd);
+ }
+
return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL);
}