]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/bluetooth/mgmt.c
Bluetooth: Remove unnecessary stop_scan_complete function
[mirror_ubuntu-artful-kernel.git] / net / bluetooth / mgmt.c
index cfcaf97c998b57742bb50dde2def44a448668df6..98e9df3556e71504063b4ac3c59d4f6775cb2451 100644 (file)
@@ -840,6 +840,13 @@ static void enable_advertising(struct hci_request *req)
        u8 own_addr_type, enable = 0x01;
        bool connectable;
 
+       /* Clear the HCI_ADVERTISING bit temporarily so that the
+        * hci_update_random_address knows that it's safe to go ahead
+        * and write a new random address. The flag will be set back on
+        * as soon as the SET_ADV_ENABLE HCI command completes.
+        */
+       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+
        connectable = get_connectable(hdev);
 
        /* Set require_privacy to true only when non-connectable
@@ -1031,8 +1038,10 @@ static void clean_up_hci_complete(struct hci_dev *hdev, u8 status)
 {
        BT_DBG("%s status 0x%02x", hdev->name, status);
 
-       if (hci_conn_count(hdev) == 0)
+       if (hci_conn_count(hdev) == 0) {
+               cancel_delayed_work(&hdev->power_off);
                queue_work(hdev->req_workqueue, &hdev->power_off.work);
+       }
 }
 
 static int clean_up_hci_state(struct hci_dev *hdev)
@@ -1057,10 +1066,34 @@ static int clean_up_hci_state(struct hci_dev *hdev)
 
        list_for_each_entry(conn, &hdev->conn_hash.list, list) {
                struct hci_cp_disconnect dc;
-
-               dc.handle = cpu_to_le16(conn->handle);
-               dc.reason = 0x15; /* Terminated due to Power Off */
-               hci_req_add(&req, HCI_OP_DISCONNECT, sizeof(dc), &dc);
+               struct hci_cp_reject_conn_req rej;
+
+               switch (conn->state) {
+               case BT_CONNECTED:
+               case BT_CONFIG:
+                       dc.handle = cpu_to_le16(conn->handle);
+                       dc.reason = 0x15; /* Terminated due to Power Off */
+                       hci_req_add(&req, HCI_OP_DISCONNECT, sizeof(dc), &dc);
+                       break;
+               case BT_CONNECT:
+                       if (conn->type == LE_LINK)
+                               hci_req_add(&req, HCI_OP_LE_CREATE_CONN_CANCEL,
+                                           0, NULL);
+                       else if (conn->type == ACL_LINK)
+                               hci_req_add(&req, HCI_OP_CREATE_CONN_CANCEL,
+                                           6, &conn->dst);
+                       break;
+               case BT_CONNECT2:
+                       bacpy(&rej.bdaddr, &conn->dst);
+                       rej.reason = 0x15; /* Terminated due to Power Off */
+                       if (conn->type == ACL_LINK)
+                               hci_req_add(&req, HCI_OP_REJECT_CONN_REQ,
+                                           sizeof(rej), &rej);
+                       else if (conn->type == SCO_LINK)
+                               hci_req_add(&req, HCI_OP_REJECT_SYNC_CONN_REQ,
+                                           sizeof(rej), &rej);
+                       break;
+               }
        }
 
        return hci_req_run(&req, clean_up_hci_complete);
@@ -1115,9 +1148,13 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
        } else {
                /* Disconnect connections, stop scans, etc */
                err = clean_up_hci_state(hdev);
+               if (!err)
+                       queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
+                                          HCI_POWER_OFF_TIMEOUT);
 
                /* ENODATA means there were no HCI commands queued */
                if (err == -ENODATA) {
+                       cancel_delayed_work(&hdev->power_off);
                        queue_work(hdev->req_workqueue, &hdev->power_off.work);
                        err = 0;
                }
@@ -2416,6 +2453,8 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
                hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type);
 
+               hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type);
+
                err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type);
        }
 
@@ -2815,12 +2854,22 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
        else
                auth_type = HCI_AT_DEDICATED_BONDING_MITM;
 
-       if (cp->addr.type == BDADDR_BREDR)
-               conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr,
-                                  cp->addr.type, sec_level, auth_type);
-       else
-               conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr,
-                                  cp->addr.type, sec_level, auth_type);
+       if (cp->addr.type == BDADDR_BREDR) {
+               conn = hci_connect_acl(hdev, &cp->addr.bdaddr, sec_level,
+                                      auth_type);
+       } else {
+               u8 addr_type;
+
+               /* Convert from L2CAP channel address type to HCI address type
+                */
+               if (cp->addr.type == BDADDR_LE_PUBLIC)
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       addr_type = ADDR_LE_DEV_RANDOM;
+
+               conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type,
+                                     sec_level, auth_type);
+       }
 
        if (IS_ERR(conn)) {
                int status;
@@ -3429,12 +3478,12 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                        goto failed;
                }
 
-               if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
-                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-                                        MGMT_STATUS_BUSY);
-                       mgmt_pending_remove(cmd);
-                       goto failed;
-               }
+               /* If controller is scanning, it means the background scanning
+                * is running. Thus, we should temporarily stop it in order to
+                * set the discovery scanning parameters.
+                */
+               if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+                       hci_req_add_le_scan_disable(&req);
 
                memset(&param_cp, 0, sizeof(param_cp));
 
@@ -3615,15 +3664,17 @@ static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data,
        hci_dev_lock(hdev);
 
        if (!hci_discovery_active(hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
-                                MGMT_STATUS_FAILED);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
+                                  MGMT_STATUS_FAILED, &cp->addr,
+                                  sizeof(cp->addr));
                goto failed;
        }
 
        e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr);
        if (!e) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
-                                MGMT_STATUS_INVALID_PARAMS);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
+                                  MGMT_STATUS_INVALID_PARAMS, &cp->addr,
+                                  sizeof(cp->addr));
                goto failed;
        }
 
@@ -3912,6 +3963,21 @@ static int set_scan_params(struct sock *sk, struct hci_dev *hdev,
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 0, NULL, 0);
 
+       /* If background scan is running, restart it so new parameters are
+        * loaded.
+        */
+       if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+           hdev->discovery.state == DISCOVERY_STOPPED) {
+               struct hci_request req;
+
+               hci_req_init(&req, hdev);
+
+               hci_req_add_le_scan_disable(&req);
+               hci_req_add_le_passive_scan(&req);
+
+               hci_req_run(&req, NULL);
+       }
+
        hci_dev_unlock(hdev);
 
        return err;
@@ -4659,6 +4725,17 @@ void mgmt_index_removed(struct hci_dev *hdev)
        mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
 }
 
+/* This function requires the caller holds hdev->lock */
+static void restart_le_auto_conns(struct hci_dev *hdev)
+{
+       struct hci_conn_params *p;
+
+       list_for_each_entry(p, &hdev->le_conn_params, list) {
+               if (p->auto_connect == HCI_AUTO_CONN_ALWAYS)
+                       hci_pend_le_conn_add(hdev, &p->addr, p->addr_type);
+       }
+}
+
 static void powered_complete(struct hci_dev *hdev, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
@@ -4667,6 +4744,8 @@ static void powered_complete(struct hci_dev *hdev, u8 status)
 
        hci_dev_lock(hdev);
 
+       restart_le_auto_conns(hdev);
+
        mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
 
        new_settings(hdev, match.sk);
@@ -4953,11 +5032,11 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key)
        ev.key.type = key->authenticated;
        ev.key.enc_size = key->enc_size;
        ev.key.ediv = key->ediv;
+       ev.key.rand = key->rand;
 
        if (key->type == HCI_SMP_LTK)
                ev.key.master = 1;
 
-       memcpy(ev.key.rand, key->rand, sizeof(key->rand));
        memcpy(ev.key.val, key->val, sizeof(key->val));
 
        mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
@@ -5081,8 +5160,10 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
                /* The connection is still in hci_conn_hash so test for 1
                 * instead of 0 to know if this is the last one.
                 */
-               if (!cp->val && hci_conn_count(hdev) == 1)
+               if (!cp->val && hci_conn_count(hdev) == 1) {
+                       cancel_delayed_work(&hdev->power_off);
                        queue_work(hdev->req_workqueue, &hdev->power_off.work);
+               }
        }
 
        if (!mgmt_connected)
@@ -5142,6 +5223,20 @@ void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                         u8 addr_type, u8 status)
 {
        struct mgmt_ev_connect_failed ev;
+       struct pending_cmd *power_off;
+
+       power_off = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
+       if (power_off) {
+               struct mgmt_mode *cp = power_off->param;
+
+               /* The connection is still in hci_conn_hash so test for 1
+                * instead of 0 to know if this is the last one.
+                */
+               if (!cp->val && hci_conn_count(hdev) == 1) {
+                       cancel_delayed_work(&hdev->power_off);
+                       queue_work(hdev->req_workqueue, &hdev->power_off.work);
+               }
+       }
 
        bacpy(&ev.addr.bdaddr, bdaddr);
        ev.addr.type = link_to_bdaddr(link_type, addr_type);