]> exis.tech > repos - linux.git/blobdiff - net/bluetooth/mgmt.c
Merge tag 'hwmon-for-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groec...
[linux.git] / net / bluetooth / mgmt.c
index 733a4b70e10c581b6c7864e92df905a0b437ef38..1db10e0f617f92cdde51cf8cd5f618843a21e724 100644 (file)
@@ -3091,6 +3091,8 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
        struct mgmt_cp_unpair_device *cp = cmd->param;
        struct hci_conn *conn;
 
+       hci_dev_lock(hdev);
+
        if (cp->addr.type == BDADDR_BREDR)
                conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
                                               &cp->addr.bdaddr);
@@ -3098,6 +3100,11 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
                conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
                                               le_addr_type(cp->addr.type));
 
+       if (conn)
+               hci_conn_get(conn);
+
+       hci_dev_unlock(hdev);
+
        if (!conn)
                return 0;
 
@@ -3105,6 +3112,7 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
         * will clean up the connection no matter the error.
         */
        hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+       hci_conn_put(conn);
 
        return 0;
 }
@@ -3252,6 +3260,8 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
        struct mgmt_cp_disconnect *cp = cmd->param;
        struct hci_conn *conn;
 
+       hci_dev_lock(hdev);
+
        if (cp->addr.type == BDADDR_BREDR)
                conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
                                               &cp->addr.bdaddr);
@@ -3259,6 +3269,11 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
                conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
                                               le_addr_type(cp->addr.type));
 
+       if (conn)
+               hci_conn_get(conn);
+
+       hci_dev_unlock(hdev);
+
        if (!conn)
                return -ENOTCONN;
 
@@ -3266,6 +3281,7 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
         * will clean up the connection no matter the error.
         */
        hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+       hci_conn_put(conn);
 
        return 0;
 }
@@ -7388,6 +7404,9 @@ static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err)
                rp.max_tx_power = HCI_TX_POWER_INVALID;
        }
 
+       if (conn)
+               hci_conn_put(conn);
+
        mgmt_cmd_complete(cmd->sk, cmd->hdev->id, MGMT_OP_GET_CONN_INFO, status,
                          &rp, sizeof(rp));
 
@@ -7402,6 +7421,8 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data)
        int err;
        __le16   handle;
 
+       hci_dev_lock(hdev);
+
        /* Make sure we are still connected */
        if (cp->addr.type == BDADDR_BREDR)
                conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
@@ -7409,12 +7430,16 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data)
        else
                conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
 
-       if (!conn || conn->state != BT_CONNECTED)
+       if (!conn || conn->state != BT_CONNECTED) {
+               hci_dev_unlock(hdev);
                return MGMT_STATUS_NOT_CONNECTED;
+       }
 
-       cmd->user_data = conn;
+       cmd->user_data = hci_conn_get(conn);
        handle = cpu_to_le16(conn->handle);
 
+       hci_dev_unlock(hdev);
+
        /* Refresh RSSI each time */
        err = hci_read_rssi_sync(hdev, handle);
 
@@ -7548,6 +7573,9 @@ static void get_clock_info_complete(struct hci_dev *hdev, void *data, int err)
        }
 
 complete:
+       if (conn)
+               hci_conn_put(conn);
+
        mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, status, &rp,
                          sizeof(rp));
 
@@ -7564,15 +7592,21 @@ static int get_clock_info_sync(struct hci_dev *hdev, void *data)
        memset(&hci_cp, 0, sizeof(hci_cp));
        hci_read_clock_sync(hdev, &hci_cp);
 
+       hci_dev_lock(hdev);
+
        /* Make sure connection still exists */
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
-       if (!conn || conn->state != BT_CONNECTED)
+       if (!conn || conn->state != BT_CONNECTED) {
+               hci_dev_unlock(hdev);
                return MGMT_STATUS_NOT_CONNECTED;
+       }
 
-       cmd->user_data = conn;
+       cmd->user_data = hci_conn_get(conn);
        hci_cp.handle = cpu_to_le16(conn->handle);
        hci_cp.which = 0x01; /* Piconet clock */
 
+       hci_dev_unlock(hdev);
+
        return hci_read_clock_sync(hdev, &hci_cp);
 }
 
@@ -7937,14 +7971,36 @@ unlock:
 
 static int conn_update_sync(struct hci_dev *hdev, void *data)
 {
-       struct hci_conn_params *params = data;
-       struct hci_conn *conn;
+       struct hci_conn *conn = data;
+       struct hci_conn_params *params;
+       struct hci_conn_params local = {};
 
-       conn = hci_conn_hash_lookup_le(hdev, &params->addr, params->addr_type);
-       if (!conn)
-               return -ECANCELED;
+       hci_dev_lock(hdev);
+
+       if (!hci_conn_valid(hdev, conn) || conn->role != HCI_ROLE_MASTER)
+               goto cancel;
+
+       params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+       if (!params)
+               goto cancel;
+
+       local.conn_min_interval = params->conn_min_interval;
+       local.conn_max_interval = params->conn_max_interval;
+       local.conn_latency = params->conn_latency;
+       local.supervision_timeout = params->supervision_timeout;
+
+       hci_dev_unlock(hdev);
+
+       return hci_le_conn_update_sync(hdev, conn, &local);
 
-       return hci_le_conn_update_sync(hdev, conn, params);
+cancel:
+       hci_dev_unlock(hdev);
+       return -ECANCELED;
+}
+
+static void conn_update_sync_destroy(struct hci_dev *hdev, void *data, int err)
+{
+       hci_conn_put(data);
 }
 
 static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -8054,9 +8110,13 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
                            (conn->le_conn_min_interval != min ||
                             conn->le_conn_max_interval != max ||
                             conn->le_conn_latency != latency ||
-                            conn->le_supv_timeout != timeout))
-                               hci_cmd_sync_queue(hdev, conn_update_sync,
-                                                  hci_param, NULL);
+                            conn->le_supv_timeout != timeout)) {
+                               hci_conn_get(conn);
+                               if (hci_cmd_sync_queue(hdev, conn_update_sync,
+                                                      conn,
+                                                      conn_update_sync_destroy) < 0)
+                                       hci_conn_put(conn);
+                       }
                }
        }
 
@@ -9848,6 +9908,22 @@ bool mgmt_powering_down(struct hci_dev *hdev)
        return false;
 }
 
+u8 hci_to_mgmt_reason(u8 err)
+{
+       switch (err) {
+       case HCI_ERROR_CONNECTION_TIMEOUT:
+               return MGMT_DEV_DISCONN_TIMEOUT;
+       case HCI_ERROR_REMOTE_USER_TERM:
+       case HCI_ERROR_REMOTE_LOW_RESOURCES:
+       case HCI_ERROR_REMOTE_POWER_OFF:
+               return MGMT_DEV_DISCONN_REMOTE;
+       case HCI_ERROR_LOCAL_HOST_TERM:
+               return MGMT_DEV_DISCONN_LOCAL_HOST;
+       default:
+               return MGMT_DEV_DISCONN_UNKNOWN;
+       }
+}
+
 void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
                              u8 link_type, u8 addr_type, u8 reason,
                              bool mgmt_connected)
@@ -9909,7 +9985,8 @@ void mgmt_connect_failed(struct hci_dev *hdev, struct hci_conn *conn, u8 status)
 
        if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
                mgmt_device_disconnected(hdev, &conn->dst, conn->type,
-                                        conn->dst_type, status, true);
+                                        conn->dst_type,
+                                        hci_to_mgmt_reason(status), true);
                return;
        }