diff --git a/include/bluetooth/conn.h b/include/bluetooth/conn.h index 270d0bc9f12..cfaf4a10e9b 100644 --- a/include/bluetooth/conn.h +++ b/include/bluetooth/conn.h @@ -318,6 +318,30 @@ struct bt_conn_cb { */ void (*disconnected)(struct bt_conn *conn, uint8_t reason); + /** @brief LE connection parameter update request. + * + * This callback notifies the application that a remote device + * is requesting to update the connection parameters. The + * application accepts the parameters by returning true, or + * rejects them by returning false. Before accepting, the + * application may also adjust the parameters to better suit + * its needs. + * + * It is recommended for an application to have just one of these + * callbacks for simplicity. However, if an application registers + * multiple it needs to manage the potentially different + * requirements for each callback. Each callback gets the + * parameters as returned by previous callbacks, i.e. they are not + * necessarily the same ones as the remote originally sent. + * + * @param conn Connection object. + * @param param Proposed connection parameters. + * + * @return true to accept the parameters, or false to reject them. + */ + bool (*le_param_req)(struct bt_conn *conn, + struct bt_le_conn_param *param); + /** @brief The parameters for an LE connection have been updated. * * This callback notifies the application that the connection diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 5d3cd36ce95..720cba456c1 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -130,6 +130,35 @@ void notify_le_param_updated(struct bt_conn *conn) } } +bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param) +{ + struct bt_conn_cb *cb; + + if (!bt_le_conn_params_valid(param)) { + return false; + } + + for (cb = callback_list; cb; cb = cb->_next) { + if (!cb->le_param_req) { + continue; + } + + if (!cb->le_param_req(conn, param)) { + return false; + } + + /* The callback may modify the parameters so we need to + * double-check that it returned valid parameters. + */ + if (!bt_le_conn_params_valid(param)) { + return false; + } + } + + /* Default to accepting if there's no app callback */ + return true; +} + static void le_conn_update(struct k_work *work) { struct bt_conn_le *le = CONTAINER_OF(work, struct bt_conn_le, diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 5a15b21e525..129be5dd94c 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -167,6 +167,8 @@ int bt_conn_le_conn_update(struct bt_conn *conn, void notify_le_param_updated(struct bt_conn *conn); +bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param); + #if defined(CONFIG_BLUETOOTH_SMP) /* rand and ediv should be in BT order */ int bt_conn_le_start_encryption(struct bt_conn *conn, uint64_t rand, diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 67d06393c6e..662bfb60211 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -853,6 +853,7 @@ static int le_conn_param_req(struct net_buf *buf) struct bt_le_conn_param param; struct bt_conn *conn; uint16_t handle; + int err; handle = sys_le16_to_cpu(evt->handle); param.interval_min = sys_le16_to_cpu(evt->interval_min); @@ -867,14 +868,15 @@ static int le_conn_param_req(struct net_buf *buf) BT_HCI_ERR_UNKNOWN_CONN_ID); } - bt_conn_unref(conn); - - if (!bt_le_conn_params_valid(¶m)) { - return le_conn_param_neg_reply(handle, - BT_HCI_ERR_INVALID_LL_PARAMS); + if (!le_param_req(conn, ¶m)) { + err = le_conn_param_neg_reply(handle, + BT_HCI_ERR_INVALID_LL_PARAMS); + } else { + err = le_conn_param_req_reply(handle, ¶m); } - return le_conn_param_req_reply(handle, ¶m); + bt_conn_unref(conn); + return err; } static void le_conn_update_complete(struct net_buf *buf) diff --git a/subsys/bluetooth/host/l2cap.c b/subsys/bluetooth/host/l2cap.c index b857dc8f754..dffbfbfbe91 100644 --- a/subsys/bluetooth/host/l2cap.c +++ b/subsys/bluetooth/host/l2cap.c @@ -554,9 +554,9 @@ static void le_conn_param_update_req(struct bt_l2cap *l2cap, uint8_t ident, { struct bt_conn *conn = l2cap->chan.chan.conn; struct bt_le_conn_param param; - bool params_valid; struct bt_l2cap_conn_param_rsp *rsp; struct bt_l2cap_conn_param_req *req = (void *)buf->data; + bool accepted; if (buf->len < sizeof(*req)) { BT_ERR("Too small LE conn update param req"); @@ -584,10 +584,10 @@ static void le_conn_param_update_req(struct bt_l2cap *l2cap, uint8_t ident, return; } - params_valid = bt_le_conn_params_valid(¶m); + accepted = le_param_req(conn, ¶m); rsp = net_buf_add(buf, sizeof(*rsp)); - if (params_valid) { + if (accepted) { rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_ACCEPTED); } else { rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_REJECTED); @@ -595,7 +595,7 @@ static void le_conn_param_update_req(struct bt_l2cap *l2cap, uint8_t ident, bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); - if (params_valid) { + if (accepted) { bt_conn_le_conn_update(conn, ¶m); } }