Bluetooth: Defer connection-parameter removal when unpairing
authorAlfonso Acosta <fons@spotify.com>
Sat, 11 Oct 2014 21:44:47 +0000 (21:44 +0000)
committerMarcel Holtmann <marcel@holtmann.org>
Sat, 25 Oct 2014 05:56:24 +0000 (07:56 +0200)
Systematically removing the LE connection parameters and autoconnect
action is inconvenient for rebonding without disconnecting from
userland (i.e. unpairing followed by repairing without
disconnecting). The parameters will be lost after unparing and
userland needs to take care of book-keeping them and re-adding them.

This patch allows userland to forget about parameter management when
rebonding without disconnecting. It defers clearing the connection
parameters when unparing without disconnecting, giving a chance of
keeping the parameters if a repairing happens before the connection is
closed.

Signed-off-by: Alfonso Acosta <fons@spotify.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_conn.c
net/bluetooth/mgmt.c

index 07ddeed624194594120f6d84c25b375278954574..b8685a77a15e0efc1dc55cd460bd06fd94dcc149 100644 (file)
@@ -555,6 +555,7 @@ enum {
        HCI_CONN_STK_ENCRYPT,
        HCI_CONN_AUTH_INITIATOR,
        HCI_CONN_DROP,
+       HCI_CONN_PARAM_REMOVAL_PEND,
 };
 
 static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
index b9517bd171901e7c6a9395791f3ecfc7fbefdd6d..11aac06d53ced7bf2e7d0cddf3a17b9b185076a9 100644 (file)
@@ -544,6 +544,9 @@ int hci_conn_del(struct hci_conn *conn)
 
        hci_conn_del_sysfs(conn);
 
+       if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
+               hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
+
        hci_dev_put(hdev);
 
        hci_conn_put(conn);
index 3fd88b06ed5a0229943c8fa96f7896d24bb8666b..9c4daf715cf804ed3e23b240f793ea3410f73a4b 100644 (file)
@@ -2725,10 +2725,40 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
        }
 
        if (cp->addr.type == BDADDR_BREDR) {
+               /* If disconnection is requested, then look up the
+                * connection. If the remote device is connected, it
+                * will be later used to terminate the link.
+                *
+                * Setting it to NULL explicitly will cause no
+                * termination of the link.
+                */
+               if (cp->disconnect)
+                       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+                                                      &cp->addr.bdaddr);
+               else
+                       conn = NULL;
+
                err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
        } else {
                u8 addr_type;
 
+               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
+                                              &cp->addr.bdaddr);
+               if (conn) {
+                       /* Defer clearing up the connection parameters
+                        * until closing to give a chance of keeping
+                        * them if a repairing happens.
+                        */
+                       set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
+
+                       /* If disconnection is not requested, then
+                        * clear the connection variable so that the
+                        * link is not terminated.
+                        */
+                       if (!cp->disconnect)
+                               conn = NULL;
+               }
+
                if (cp->addr.type == BDADDR_LE_PUBLIC)
                        addr_type = ADDR_LE_DEV_PUBLIC;
                else
@@ -2736,8 +2766,6 @@ 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);
        }
 
@@ -2747,17 +2775,9 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
-       if (cp->disconnect) {
-               if (cp->addr.type == BDADDR_BREDR)
-                       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
-                                                      &cp->addr.bdaddr);
-               else
-                       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
-                                                      &cp->addr.bdaddr);
-       } else {
-               conn = NULL;
-       }
-
+       /* If the connection variable is set, then termination of the
+        * link is requested.
+        */
        if (!conn) {
                err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0,
                                   &rp, sizeof(rp));
@@ -3062,6 +3082,11 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
        hci_conn_put(conn);
 
        mgmt_pending_remove(cmd);
+
+       /* The device is paired so there is no need to remove
+        * its connection parameters anymore.
+        */
+       clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
 }
 
 void mgmt_smp_complete(struct hci_conn *conn, bool complete)