Bluetooth: MGMT: Make MGMT_OP_LOAD_CONN_PARAM update existing connection
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 10 May 2024 14:36:45 +0000 (10:36 -0400)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 15 Jul 2024 01:33:24 +0000 (21:33 -0400)
This makes MGMT_OP_LOAD_CONN_PARAM update existing connection by
dectecting the request is just for one connection, parameters already
exists and there is a connection.

Since this is a new behavior the revision is also updated to enable
userspace to detect it.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
include/net/bluetooth/hci_sync.h
net/bluetooth/hci_sync.c
net/bluetooth/mgmt.c

index 534c3386e714f9e46984e9596f0216ba5fc192d9..20168732f20e48be0a921aa7f314678805f8a474 100644 (file)
@@ -138,6 +138,7 @@ int hci_suspend_sync(struct hci_dev *hdev);
 int hci_resume_sync(struct hci_dev *hdev);
 
 struct hci_conn;
+struct hci_conn_params;
 
 int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason);
 
@@ -156,3 +157,5 @@ int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn);
 int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn);
 
 int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn);
+int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
+                           struct hci_conn_params *params);
index eea34e6a236fd17763bfce47aeeb2f7152510dfe..82db6092965b2dfcedf78bfe4e2065ecec5b219c 100644 (file)
@@ -6724,3 +6724,21 @@ int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
 
        return -ENOENT;
 }
+
+int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
+                           struct hci_conn_params *params)
+{
+       struct hci_cp_le_conn_update cp;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle               = cpu_to_le16(conn->handle);
+       cp.conn_interval_min    = cpu_to_le16(params->conn_min_interval);
+       cp.conn_interval_max    = cpu_to_le16(params->conn_max_interval);
+       cp.conn_latency         = cpu_to_le16(params->conn_latency);
+       cp.supervision_timeout  = cpu_to_le16(params->supervision_timeout);
+       cp.min_ce_len           = cpu_to_le16(0x0000);
+       cp.max_ce_len           = cpu_to_le16(0x0000);
+
+       return __hci_cmd_sync_status(hdev, HCI_OP_LE_CONN_UPDATE,
+                                    sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
index 80f220b7e19d5560df42e807c20c70622e3bc205..20eca8a9c681d8c69e5eec5ee68b4c65739db4f2 100644 (file)
@@ -42,7 +42,7 @@
 #include "aosp.h"
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  22
+#define MGMT_REVISION  23
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -7813,6 +7813,18 @@ unlock:
        return err;
 }
 
+static int conn_update_sync(struct hci_dev *hdev, void *data)
+{
+       struct hci_conn_params *params = data;
+       struct hci_conn *conn;
+
+       conn = hci_conn_hash_lookup_le(hdev, &params->addr, params->addr_type);
+       if (!conn)
+               return -ECANCELED;
+
+       return hci_le_conn_update_sync(hdev, conn, params);
+}
+
 static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
                           u16 len)
 {
@@ -7846,13 +7858,15 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
 
        hci_dev_lock(hdev);
 
-       hci_conn_params_clear_disabled(hdev);
+       if (param_count > 1)
+               hci_conn_params_clear_disabled(hdev);
 
        for (i = 0; i < param_count; i++) {
                struct mgmt_conn_param *param = &cp->params[i];
                struct hci_conn_params *hci_param;
                u16 min, max, latency, timeout;
                u8 addr_type;
+               bool update;
 
                bt_dev_dbg(hdev, "Adding %pMR (type %u)", &param->addr.bdaddr,
                           param->addr.type);
@@ -7879,6 +7893,19 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
                        continue;
                }
 
+               /* Detect when the loading is for an existing parameter then
+                * attempt to trigger the connection update procedure.
+                */
+               if (!i && param_count == 1) {
+                       hci_param = hci_conn_params_lookup(hdev,
+                                                          &param->addr.bdaddr,
+                                                          addr_type);
+                       if (hci_param)
+                               update = true;
+                       else
+                               hci_conn_params_clear_disabled(hdev);
+               }
+
                hci_param = hci_conn_params_add(hdev, &param->addr.bdaddr,
                                                addr_type);
                if (!hci_param) {
@@ -7890,6 +7917,25 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
                hci_param->conn_max_interval = max;
                hci_param->conn_latency = latency;
                hci_param->supervision_timeout = timeout;
+
+               /* Check if we need to trigger a connection update */
+               if (update) {
+                       struct hci_conn *conn;
+
+                       /* Lookup for existing connection as central and check
+                        * if parameters match and if they don't then trigger
+                        * a connection update.
+                        */
+                       conn = hci_conn_hash_lookup_le(hdev, &hci_param->addr,
+                                                      addr_type);
+                       if (conn && conn->role == HCI_ROLE_MASTER &&
+                           (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);
+               }
        }
 
        hci_dev_unlock(hdev);