From b6979010c6afe131cb62fb298789fa1466849c2c Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Tue, 6 Nov 2018 13:48:07 +0100 Subject: [PATCH] Bluetooth: host: Fallback to L2CAP for CPUP if rejected by master on LL Fallback to L2CAP Connection Parameters Update Request if LL Connection Update Request was rejected by remote device that has this marked as supported in features. This can happen if procedure is supported only by remote controller, but not enabled by host. This is connection parameters update with iOS devices. Signed-off-by: Szymon Janc --- subsys/bluetooth/host/conn.c | 29 ++++++++++++++++++--------- subsys/bluetooth/host/conn_internal.h | 3 +++ subsys/bluetooth/host/hci_core.c | 12 +++++++++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index e4fe027e30b8..45e1d534fa87 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -202,9 +202,20 @@ static int send_conn_le_param_update(struct bt_conn *conn, * 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); + BT_FEAT_LE_CONN_PARAM_REQ_PROC(conn->le.features) && + !atomic_test_bit(conn->flags, BT_CONN_SLAVE_PARAM_L2CAP)) || + (conn->role == BT_HCI_ROLE_MASTER)) { + int rc; + + rc = bt_conn_le_conn_update(conn, param); + + /* store those in case of fallback to L2CAP */ + if (rc == 0) { + conn->le.pending_latency = param->latency; + conn->le.pending_timeout = param->timeout; + } + + return rc; } /* If remote master does not support LL Connection Parameters Request @@ -237,8 +248,8 @@ static void conn_le_update_timeout(struct k_work *work) if (atomic_test_and_clear_bit(conn->flags, BT_CONN_SLAVE_PARAM_SET)) { param = BT_LE_CONN_PARAM(conn->le.interval_min, conn->le.interval_max, - conn->le.latency, - conn->le.timeout); + conn->le.pending_latency, + conn->le.pending_timeout); send_conn_le_param_update(conn, param); } else { @@ -1814,13 +1825,11 @@ int bt_conn_le_param_update(struct bt_conn *conn, 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 - */ + /* store new conn params to be used by update timer */ conn->le.interval_min = param->interval_min; conn->le.interval_max = param->interval_max; - conn->le.latency = param->latency; - conn->le.timeout = param->timeout; + conn->le.pending_latency = param->latency; + conn->le.pending_timeout = param->timeout; atomic_set_bit(conn->flags, BT_CONN_SLAVE_PARAM_SET); } diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 887e163c6739..f853917b0a77 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -29,6 +29,7 @@ enum { BT_CONN_AUTO_DATA_LEN, /* Auto data len change in progress */ BT_CONN_SLAVE_PARAM_UPDATE, /* If slave param update timer fired */ BT_CONN_SLAVE_PARAM_SET, /* If slave param were set from app */ + BT_CONN_SLAVE_PARAM_L2CAP, /* If should force L2CAP for CPUP */ /* Total number of flags - must be at the end of the enum */ BT_CONN_NUM_FLAGS, @@ -46,6 +47,8 @@ struct bt_conn_le { u16_t latency; u16_t timeout; + u16_t pending_latency; + u16_t pending_timeout; u8_t features[8]; diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 42df3f812b49..7bceaedad463 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -1241,6 +1241,18 @@ static void le_conn_update_complete(struct net_buf *buf) conn->le.latency = sys_le16_to_cpu(evt->latency); conn->le.timeout = sys_le16_to_cpu(evt->supv_timeout); notify_le_param_updated(conn); + } else if (evt->status == BT_HCI_ERR_UNSUPP_REMOTE_FEATURE && + conn->role == BT_HCI_ROLE_SLAVE && + !atomic_test_and_set_bit(conn->flags, + BT_CONN_SLAVE_PARAM_L2CAP)) { + struct bt_le_conn_param param; + + param.interval_min = conn->le.interval_min; + param.interval_max = conn->le.interval_max; + param.latency = conn->le.pending_latency; + param.timeout = conn->le.pending_timeout; + + bt_l2cap_update_conn_param(conn, ¶m); } bt_conn_unref(conn);