]> www.infradead.org Git - users/willy/xarray.git/commitdiff
Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 26 Aug 2024 20:14:04 +0000 (16:14 -0400)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 30 Aug 2024 21:56:34 +0000 (17:56 -0400)
MGMT_OP_DISCONNECT can be called while mgmt_device_connected has not
been called yet, which will cause the connection procedure to be
aborted, so mgmt_device_disconnected shall still respond with command
complete to MGMT_OP_DISCONNECT and just not emit
MGMT_EV_DEVICE_DISCONNECTED since MGMT_EV_DEVICE_CONNECTED was never
sent.

To fix this MGMT_OP_DISCONNECT is changed to work similarly to other
command which do use hci_cmd_sync_queue and then use hci_conn_abort to
disconnect and returns the result, in order for hci_conn_abort to be
used from hci_cmd_sync context it now uses hci_cmd_sync_run_once.

Link: https://github.com/bluez/bluez/issues/932
Fixes: 12d4a3b2ccb3 ("Bluetooth: Move check for MGMT_CONNECTED flag into mgmt.c")
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/hci_conn.c
net/bluetooth/mgmt.c

index 8e48ccd2af30e20b41b0358986a3b55e4213eff9..c82502e213a88a2623c643091f963aa6402e75ad 100644 (file)
@@ -2952,5 +2952,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
                return 0;
        }
 
-       return hci_cmd_sync_queue_once(hdev, abort_conn_sync, conn, NULL);
+       /* Run immediately if on cmd_sync_work since this may be called
+        * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
+        * already queue its callback on cmd_sync_work.
+        */
+       return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
 }
index 25979f4283a6ff413a2cb49b7f8b677dba1569de..4c20dbf92c71d5b87a27c0036b6ed8d4d7aeea40 100644 (file)
@@ -2921,7 +2921,12 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
        if (!conn)
                return 0;
 
-       return hci_abort_conn_sync(hdev, conn, HCI_ERROR_REMOTE_USER_TERM);
+       /* Disregard any possible error since the likes of hci_abort_conn_sync
+        * will clean up the connection no matter the error.
+        */
+       hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+
+       return 0;
 }
 
 static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -3053,13 +3058,44 @@ unlock:
        return err;
 }
 
+static void disconnect_complete(struct hci_dev *hdev, void *data, int err)
+{
+       struct mgmt_pending_cmd *cmd = data;
+
+       cmd->cmd_complete(cmd, mgmt_status(err));
+       mgmt_pending_free(cmd);
+}
+
+static int disconnect_sync(struct hci_dev *hdev, void *data)
+{
+       struct mgmt_pending_cmd *cmd = data;
+       struct mgmt_cp_disconnect *cp = cmd->param;
+       struct hci_conn *conn;
+
+       if (cp->addr.type == BDADDR_BREDR)
+               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+                                              &cp->addr.bdaddr);
+       else
+               conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
+                                              le_addr_type(cp->addr.type));
+
+       if (!conn)
+               return -ENOTCONN;
+
+       /* Disregard any possible error since the likes of hci_abort_conn_sync
+        * will clean up the connection no matter the error.
+        */
+       hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+
+       return 0;
+}
+
 static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
                      u16 len)
 {
        struct mgmt_cp_disconnect *cp = data;
        struct mgmt_rp_disconnect rp;
        struct mgmt_pending_cmd *cmd;
-       struct hci_conn *conn;
        int err;
 
        bt_dev_dbg(hdev, "sock %p", sk);
@@ -3082,27 +3118,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
-       if (pending_find(MGMT_OP_DISCONNECT, hdev)) {
-               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
-                                       MGMT_STATUS_BUSY, &rp, sizeof(rp));
-               goto failed;
-       }
-
-       if (cp->addr.type == BDADDR_BREDR)
-               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
-                                              &cp->addr.bdaddr);
-       else
-               conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
-                                              le_addr_type(cp->addr.type));
-
-       if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) {
-               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
-                                       MGMT_STATUS_NOT_CONNECTED, &rp,
-                                       sizeof(rp));
-               goto failed;
-       }
-
-       cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
+       cmd = mgmt_pending_new(sk, MGMT_OP_DISCONNECT, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
@@ -3110,9 +3126,10 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
 
        cmd->cmd_complete = generic_cmd_complete;
 
-       err = hci_disconnect(conn, HCI_ERROR_REMOTE_USER_TERM);
+       err = hci_cmd_sync_queue(hdev, disconnect_sync, cmd,
+                                disconnect_complete);
        if (err < 0)
-               mgmt_pending_remove(cmd);
+               mgmt_pending_free(cmd);
 
 failed:
        hci_dev_unlock(hdev);
@@ -9689,18 +9706,6 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
        mgmt_event_skb(skb, NULL);
 }
 
-static void disconnect_rsp(struct mgmt_pending_cmd *cmd, void *data)
-{
-       struct sock **sk = data;
-
-       cmd->cmd_complete(cmd, 0);
-
-       *sk = cmd->sk;
-       sock_hold(*sk);
-
-       mgmt_pending_remove(cmd);
-}
-
 static void unpair_device_rsp(struct mgmt_pending_cmd *cmd, void *data)
 {
        struct hci_dev *hdev = data;
@@ -9744,8 +9749,6 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
        if (link_type != ACL_LINK && link_type != LE_LINK)
                return;
 
-       mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
-
        bacpy(&ev.addr.bdaddr, bdaddr);
        ev.addr.type = link_to_bdaddr(link_type, addr_type);
        ev.reason = reason;
@@ -9758,9 +9761,6 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
 
        if (sk)
                sock_put(sk);
-
-       mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
-                            hdev);
 }
 
 void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,