Currently, hci_conn_del calls hci_conn_unlink for BR/EDR, (e)SCO, and CIS connections, i.e., everything except LE connections. However, if (e)SCO connections are unlinked when BR/EDR disconnects, CIS connections should also be unlinked when LE disconnects. In terms of disconnection behavior, CIS and (e)SCO connections are not too different. One peculiarity of CIS is that when CIS connections are disconnected, the CIS handle isn't deleted, as per [BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E] 7.1.6 Disconnect command: All SCO, eSCO, and CIS connections on a physical link should be disconnected before the ACL connection on the same physical connection is disconnected. If it does not, they will be implicitly disconnected as part of the ACL disconnection. ... Note: As specified in Section 7.7.5, on the Central, the handle for a CIS remains valid even after disconnection and, therefore, the Host can recreate a disconnected CIS at a later point in time using the same connection handle. This peculiarity has nothing to do with the code, as long as the current implementation always ties the CIS handle, the CIS connection, and the LE connection together in hci_link, and manually performs CIS deletion in cis_cleanup after CIS disconnections. Signed-off-by: Ruihan Li <lrh2000@xxxxxxxxxx> --- net/bluetooth/hci_conn.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e76ebb50d..de553e062 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1092,7 +1092,9 @@ static void hci_conn_unlink(struct hci_conn *conn) * yet at this point. Delete it now, otherwise it is * possible for it to be stuck and can't be deleted. */ - if (child->handle == HCI_CONN_HANDLE_UNSET) + if ((child->type == SCO_LINK || + child->type == ESCO_LINK) && + child->handle == HCI_CONN_HANDLE_UNSET) hci_conn_del(child); } @@ -1119,11 +1121,17 @@ void hci_conn_del(struct hci_conn *conn) BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle); + hci_conn_unlink(conn); + + /* hci_conn_unlink may trigger additional disc_work, so + * ensure to perform cancelling after that. + */ + cancel_delayed_work_sync(&conn->disc_work); + cancel_delayed_work_sync(&conn->auto_accept_work); cancel_delayed_work_sync(&conn->idle_work); if (conn->type == ACL_LINK) { - hci_conn_unlink(conn); /* Unacked frames */ hdev->acl_cnt += conn->sent; } else if (conn->type == LE_LINK) { @@ -1134,8 +1142,6 @@ void hci_conn_del(struct hci_conn *conn) else hdev->acl_cnt += conn->sent; } else { - hci_conn_unlink(conn); - /* Unacked ISO frames */ if (conn->type == ISO_LINK) { if (hdev->iso_pkts) @@ -1147,11 +1153,6 @@ void hci_conn_del(struct hci_conn *conn) } } - /* hci_conn_unlink may trigger additional disc_work, so - * ensure to perform cancelling after that. - */ - cancel_delayed_work_sync(&conn->disc_work); - if (conn->amp_mgr) amp_mgr_put(conn->amp_mgr); -- 2.40.0