Bluetooth: Fix connection parameters update

This fixes a few issues with the handling of Connection Parameter
update in the Host:
 - starting conn param update timer as master
 - ignoring 5 seconds slave timer when calling bt_conn_le_param_update
 - starting conn param update timer on every PHY update

Signed-off-by: Szymon Janc <szymon.janc@codecoup.pl>
This commit is contained in:
Szymon Janc 2018-09-24 08:18:57 +02:00 committed by Johan Hedberg
commit 75405f5613
3 changed files with 81 additions and 27 deletions

View file

@ -190,16 +190,43 @@ bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
return true;
}
static void le_conn_update(struct k_work *work)
static int send_conn_le_param_update(struct bt_conn *conn,
const struct bt_le_conn_param *param)
{
BT_DBG("conn %p features 0x%02x params (%d-%d %d %d)", conn,
conn->le.features[0], param->interval_min,
param->interval_max, param->latency, param->timeout);
/* Use LE connection parameter request if both local and remote support
* it; or if local role is master then use LE connection update.
*/
if ((BT_FEAT_LE_CONN_PARAM_REQ_PROC(bt_dev.le.features) &&
BT_FEAT_LE_CONN_PARAM_REQ_PROC(conn->le.features)) ||
(conn->role == BT_HCI_ROLE_MASTER)) {
return bt_conn_le_conn_update(conn, param);
}
/* If remote master does not support LL Connection Parameters Request
* Procedure
*/
return bt_l2cap_update_conn_param(conn, param);
}
static void conn_le_update_timeout(struct k_work *work)
{
struct bt_conn_le *le = CONTAINER_OF(work, struct bt_conn_le,
update_work);
struct bt_conn *conn = CONTAINER_OF(le, struct bt_conn, le);
const struct bt_le_conn_param *param;
BT_DBG("conn %p", conn);
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
conn->state == BT_CONN_CONNECT) {
bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
conn->role == BT_CONN_ROLE_MASTER) {
if (conn->state == BT_CONN_CONNECT) {
bt_conn_disconnect(conn,
BT_HCI_ERR_REMOTE_USER_TERM_CONN);
}
return;
}
@ -208,7 +235,9 @@ static void le_conn_update(struct k_work *work)
conn->le.latency,
conn->le.timeout);
bt_conn_le_param_update(conn, param);
send_conn_le_param_update(conn, param);
atomic_set_bit(conn->flags, BT_CONN_SLAVE_PARAM_UPDATE);
}
static struct bt_conn *conn_new(void)
@ -1385,7 +1414,7 @@ struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer)
conn->type = BT_CONN_TYPE_LE;
conn->le.interval_min = BT_GAP_INIT_CONN_INT_MIN;
conn->le.interval_max = BT_GAP_INIT_CONN_INT_MAX;
k_delayed_work_init(&conn->le.update_work, le_conn_update);
k_delayed_work_init(&conn->le.update_work, conn_le_update_timeout);
return conn;
}
@ -1749,22 +1778,27 @@ int bt_conn_le_param_update(struct bt_conn *conn,
return -EALREADY;
}
/* Cancel any pending update */
k_delayed_work_cancel(&conn->le.update_work);
/* Use LE connection parameter request if both local and remote support
* it; or if local role is master then use LE connection update.
*/
if ((BT_FEAT_LE_CONN_PARAM_REQ_PROC(bt_dev.le.features) &&
BT_FEAT_LE_CONN_PARAM_REQ_PROC(conn->le.features)) ||
(conn->role == BT_HCI_ROLE_MASTER)) {
return bt_conn_le_conn_update(conn, param);
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
conn->role == BT_CONN_ROLE_MASTER) {
return send_conn_le_param_update(conn, param);
}
/* If remote master does not support LL Connection Parameters Request
* Procedure
if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
/* if slave conn param update timer expired just send request */
if (atomic_test_bit(conn->flags, BT_CONN_SLAVE_PARAM_UPDATE)) {
return send_conn_le_param_update(conn, param);
}
/* store new conn params to be used by update timer
* TODO this overwrites current latency and timeout
*/
return bt_l2cap_update_conn_param(conn, param);
conn->le.interval_min = param->interval_min;
conn->le.interval_max = param->interval_max;
conn->le.latency = param->latency;
conn->le.timeout = param->timeout;
}
return 0;
}
int bt_conn_disconnect(struct bt_conn *conn, u8_t reason)

View file

@ -27,6 +27,7 @@ enum {
BT_CONN_CLEANUP, /* Disconnected, pending cleanup */
BT_CONN_AUTO_PHY_UPDATE, /* Auto-update PHY */
BT_CONN_AUTO_DATA_LEN, /* Auto data len change in progress */
BT_CONN_SLAVE_PARAM_UPDATE, /* If slave param update timer fired */
/* Total number of flags - must be at the end of the enum */
BT_CONN_NUM_FLAGS,

View file

@ -736,16 +736,23 @@ static int hci_le_set_phy(struct bt_conn *conn)
return 0;
}
static void update_conn_param(struct bt_conn *conn)
static void slave_update_conn_param(struct bt_conn *conn)
{
if (!IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
return;
}
/* don't start timer again on PHY update etc */
if (atomic_test_bit(conn->flags, BT_CONN_SLAVE_PARAM_UPDATE)) {
return;
}
/*
* Core 4.2 Vol 3, Part C, 9.3.12.2
* The Peripheral device should not perform a Connection Parameter
* Update procedure within 5 s after establishing a connection.
*/
k_delayed_work_submit(&conn->le.update_work,
conn->role == BT_HCI_ROLE_MASTER ? K_NO_WAIT :
CONN_UPDATE_TIMEOUT);
k_delayed_work_submit(&conn->le.update_work, CONN_UPDATE_TIMEOUT);
}
#if defined(CONFIG_BT_SMP)
@ -938,7 +945,10 @@ static void le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt)
}
}
update_conn_param(conn);
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
conn->role == BT_CONN_ROLE_SLAVE) {
slave_update_conn_param(conn);
}
done:
bt_conn_unref(conn);
@ -1029,7 +1039,10 @@ static void le_remote_feat_complete(struct net_buf *buf)
}
}
update_conn_param(conn);
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
conn->role == BT_CONN_ROLE_SLAVE) {
slave_update_conn_param(conn);
}
done:
bt_conn_unref(conn);
@ -1058,7 +1071,10 @@ static void le_data_len_change(struct net_buf *buf)
goto done;
}
update_conn_param(conn);
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
conn->role == BT_CONN_ROLE_SLAVE) {
slave_update_conn_param(conn);
}
done:
bt_conn_unref(conn);
@ -1095,7 +1111,10 @@ static void le_phy_update_complete(struct net_buf *buf)
}
}
update_conn_param(conn);
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
conn->role == BT_CONN_ROLE_SLAVE) {
slave_update_conn_param(conn);
}
done:
bt_conn_unref(conn);