Bluetooth: Fix disconnecting connections in non-connected states
authorJohan Hedberg <johan.hedberg@intel.com>
Thu, 27 Feb 2014 12:35:12 +0000 (14:35 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 28 Feb 2014 07:35:08 +0000 (23:35 -0800)
When powering off and disconnecting devices we should also consider
connections which have not yet reached the BT_CONNECTED state. They may
not have a valid handle yet and simply sending a HCI_Disconnect will not
work.

This patch updates the code to either disconnect, cancel connection
creation or reject incoming connection creation based on the current
conn->state value as well as the link type in question.

When the power off procedure results in canceling connection attempts
instead of disconnecting connections we get a connection failed event
instead of a disconnection event. Therefore, we also need to have extra
code in the mgmt_connect_failed function to check if we should proceed
with the power off or not.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
net/bluetooth/mgmt.c

index 78ac7c8640442a722514522e1230cec480091690..73b6ff817796a6538921cfec8ba50095f8e3cc42 100644 (file)
@@ -1057,10 +1057,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);
@@ -5184,6 +5208,18 @@ 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)
+                       queue_work(hdev->req_workqueue, &hdev->power_off.work);
+       }
 
        bacpy(&ev.addr.bdaddr, bdaddr);
        ev.addr.type = link_to_bdaddr(link_type, addr_type);