Skip to content

Commit 22fe768

Browse files
Troniljhedberg
authored andcommitted
Bluetooth: Controller: Various fixes for CIS termination handling
Handling of CIS termination had several issues, most notably: - it depended on allocating a termination node from the general rx node pool, causing asserts if the pool was exhausted - CIS established events was not always generated when required, potentially causing CIS Centrals to get stuck without being able to create any new CISes - Cancelling a CIS Create procedure only worked correctly if the CIS Create was currently active and happened to belong to the same CIS - CIG state handling often (always?) assumed a CIG with only one CIS ll_conn_iso_stream now has a dedicated termination node, same as ll_conn and ll_sync_iso_set LLCP statemachine for Cis Create procedure has been reworked to ensure a notification node for CIS Established is available as early as possible. In addition, it should now always be sent when needed Introduced ull_central_iso_all_cises_terminated() to check if all CISes in a CIG has been terminated (or not created yet) - which is now used for updating the CIG state ull_cp_cc_cancel() now takes the CIS to cancel as an argument so it doesn't end up canceling an entirely different CIS Create procedure; In addition it now works for queued procedures as well Flushing a (central) CIS Create procedure in LLCP will now properly generate a CIS Established event (with an error) Signed-off-by: Troels Nilsson <trnn@demant.com>
1 parent 6a0ddc3 commit 22fe768

File tree

16 files changed

+482
-112
lines changed

16 files changed

+482
-112
lines changed

subsys/bluetooth/controller/ll_sw/ull.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,12 @@ static MFIFO_DEFINE(pdu_rx_free, sizeof(void *), PDU_RX_CNT);
459459
#define BT_CTLR_SCAN_SYNC_ISO_SET 0
460460
#endif
461461

462+
#if defined(CONFIG_BT_CTLR_CONN_ISO_STREAMS)
463+
#define BT_CTLR_CONN_ISO_STREAMS CONFIG_BT_CTLR_CONN_ISO_STREAMS
464+
#else
465+
#define BT_CTLR_CONN_ISO_STREAMS 0
466+
#endif
467+
462468
#define PDU_RX_POOL_SIZE (PDU_RX_NODE_POOL_ELEMENT_SIZE * \
463469
(RX_CNT + BT_CTLR_MAX_CONNECTABLE + \
464470
BT_CTLR_ADV_SET + BT_CTLR_SCAN_SYNC_SET))
@@ -508,7 +514,7 @@ static struct {
508514
(sizeof(memq_link_t) * \
509515
(RX_CNT + 2 + BT_CTLR_MAX_CONN + BT_CTLR_ADV_SET + \
510516
(BT_CTLR_ADV_ISO_SET * 2) + (BT_CTLR_SCAN_SYNC_SET * 2) + \
511-
(BT_CTLR_SCAN_SYNC_ISO_SET * 2) + \
517+
(BT_CTLR_SCAN_SYNC_ISO_SET * 2) + BT_CTLR_CONN_ISO_STREAMS + \
512518
(IQ_REPORT_CNT)))
513519
static struct {
514520
uint16_t quota_pdu; /* Number of un-utilized buffers */
@@ -1726,8 +1732,8 @@ void ll_rx_mem_release(void **node_rx)
17261732

17271733
ll_conn_release(conn);
17281734
} else if (IS_CIS_HANDLE(rx_free->hdr.handle)) {
1729-
ll_rx_link_quota_inc();
1730-
ll_rx_release(rx_free);
1735+
/* Notify ull_conn_iso */
1736+
ull_conn_iso_cis_terminate_done(rx_free);
17311737
}
17321738
}
17331739
break;

subsys/bluetooth/controller/ll_sw/ull_central.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand_num,
559559
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
560560
struct ll_conn_iso_stream *cis = ll_conn_iso_stream_get_by_acl(conn, NULL);
561561

562-
if (cis || ull_lp_cc_is_enqueued(conn)) {
562+
if (cis || ull_lp_cc_is_enqueued(conn, NULL)) {
563563
return BT_HCI_ERR_CMD_DISALLOWED;
564564
}
565565
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */

subsys/bluetooth/controller/ll_sw/ull_central_iso.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id, uint16_t *handles)
272272

273273
/* Create all configurable CISes */
274274
for (uint8_t i = 0U; i < ll_iso_setup.cis_count; i++) {
275+
memq_link_t *link_rx_terminate;
275276
memq_link_t *link_tx_free;
276277
memq_link_t link_tx;
277278

@@ -305,9 +306,10 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id, uint16_t *handles)
305306
cig->lll.num_cis++;
306307
}
307308

308-
/* Store TX link and free link before transfer */
309+
/* Store TX link, free link and terminate link before transfer */
309310
link_tx_free = cis->lll.link_tx_free;
310311
link_tx = cis->lll.link_tx;
312+
link_rx_terminate = cis->node_rx_terminate.rx.hdr.link;
311313

312314
/* Transfer parameters from configuration cache */
313315
memcpy(cis, &ll_iso_setup.stream[i], sizeof(struct ll_conn_iso_stream));
@@ -317,6 +319,7 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id, uint16_t *handles)
317319

318320
cis->lll.link_tx_free = link_tx_free;
319321
cis->lll.link_tx = link_tx;
322+
cis->node_rx_terminate.rx.hdr.link = link_rx_terminate;
320323
cis->lll.handle = ll_conn_iso_stream_handle_get(cis);
321324
handles[i] = cis->lll.handle;
322325
}
@@ -744,6 +747,13 @@ void ll_cis_create(uint16_t cis_handle, uint16_t acl_handle)
744747

745748
(void)memset(&cis->hdr, 0U, sizeof(cis->hdr));
746749

750+
/* Allocate new terminate link if needed (only needed if CIS is re-used) */
751+
if (cis->node_rx_terminate.rx.hdr.link == NULL) {
752+
cis->node_rx_terminate.rx.hdr.link = ll_rx_link_alloc();
753+
/* Failure should not be possible, but assert just in case */
754+
LL_ASSERT_DBG(cis->node_rx_terminate.rx.hdr.link);
755+
}
756+
747757
/* Initialize TX link */
748758
if (!cis->lll.link_tx_free) {
749759
cis->lll.link_tx_free = &cis->lll.link_tx;
@@ -1022,6 +1032,30 @@ int ull_central_iso_cis_offset_get(uint16_t cis_handle,
10221032
#endif /* CONFIG_BT_CTLR_CENTRAL_SPACING != 0 */
10231033
}
10241034

1035+
bool ull_central_iso_all_cises_terminated(struct ll_conn_iso_group *cig)
1036+
{
1037+
/* Check if all CISes associated with this CIG is terminated (or not created) */
1038+
for (uint16_t handle = LL_CIS_HANDLE_BASE; handle <= LL_CIS_HANDLE_LAST; handle++) {
1039+
struct ll_conn_iso_stream *cis;
1040+
1041+
cis = ll_conn_iso_stream_get(handle);
1042+
if (cis->group == cig) {
1043+
struct ll_conn *conn;
1044+
1045+
if (cis->established) {
1046+
return false;
1047+
}
1048+
1049+
/* Check if CIS is being created */
1050+
conn = ll_connected_get(cis->lll.acl_handle);
1051+
if (conn && ull_lp_cc_is_enqueued(conn, cis)) {
1052+
return false;
1053+
}
1054+
}
1055+
}
1056+
return true;
1057+
}
1058+
10251059
#if (CONFIG_BT_CTLR_CENTRAL_SPACING == 0)
10261060
static void cig_offset_get(struct ll_conn_iso_stream *cis)
10271061
{

subsys/bluetooth/controller/ll_sw/ull_central_iso_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ uint8_t ull_central_iso_setup(uint16_t cis_handle,
1919
uint32_t *cis_offset_max,
2020
uint16_t *conn_event_count,
2121
uint8_t *access_addr);
22+
bool ull_central_iso_all_cises_terminated(struct ll_conn_iso_group *cig);

subsys/bluetooth/controller/ll_sw/ull_conn.c

Lines changed: 75 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959

6060
#include "ull_iso_internal.h"
6161
#include "ull_conn_iso_internal.h"
62+
#include "ull_central_iso_internal.h"
6263
#include "ull_peripheral_iso_internal.h"
6364
#include "lll/lll_adv_types.h"
6465
#include "lll_adv.h"
@@ -438,43 +439,44 @@ uint8_t ll_terminate_ind_send(uint16_t handle, uint8_t reason)
438439

439440
/* Sanity-check instance to make sure it's created but not connected */
440441
if (cis->group && cis->lll.handle == handle && !cis->established) {
441-
if (cis->group->state == CIG_STATE_CONFIGURABLE) {
442-
/* Disallow if CIG is still in configurable state */
443-
return BT_HCI_ERR_CMD_DISALLOWED;
442+
struct ll_conn_iso_group *cig = cis->group;
444443

445-
} else if (cis->group->state == CIG_STATE_INITIATING) {
446-
conn = ll_connected_get(cis->lll.acl_handle);
447-
LL_ASSERT_DBG(conn != NULL);
444+
conn = ll_connected_get(cis->lll.acl_handle);
448445

449-
/* CIS is not yet established - try to cancel procedure */
450-
if (ull_cp_cc_cancel(conn)) {
451-
/* Successfully canceled - complete disconnect */
452-
struct node_rx_pdu *node_terminate;
446+
if (conn == NULL || !ull_lp_cc_is_enqueued(conn, cis)) {
447+
/* Disallow if CIS is not created yet or terminated */
448+
return BT_HCI_ERR_CMD_DISALLOWED;
449+
}
453450

454-
node_terminate = ull_pdu_rx_alloc();
455-
LL_ASSERT_ERR(node_terminate);
451+
/* CIS is not yet established - try to cancel procedure */
452+
if (ull_cp_cc_cancel(conn, cis)) {
453+
/* Successfully canceled - complete disconnect */
454+
struct node_rx_pdu *node_terminate;
456455

457-
node_terminate->hdr.handle = handle;
458-
node_terminate->hdr.type = NODE_RX_TYPE_TERMINATE;
459-
*((uint8_t *)node_terminate->pdu) =
460-
BT_HCI_ERR_LOCALHOST_TERM_CONN;
456+
node_terminate = &cis->node_rx_terminate.rx;
461457

462-
ll_rx_put_sched(node_terminate->hdr.link,
463-
node_terminate);
458+
node_terminate->hdr.handle = handle;
459+
node_terminate->hdr.type = NODE_RX_TYPE_TERMINATE;
460+
*((uint8_t *)node_terminate->pdu) =
461+
BT_HCI_ERR_LOCALHOST_TERM_CONN;
464462

465-
/* We're no longer initiating a connection */
466-
cis->group->state = CIG_STATE_CONFIGURABLE;
463+
ll_rx_put_sched(node_terminate->hdr.link,
464+
node_terminate);
467465

468-
/* This is now a successful disconnection */
469-
return BT_HCI_ERR_SUCCESS;
466+
/* We're no longer initiating a connection */
467+
if (ull_central_iso_all_cises_terminated(cig)) {
468+
cig->state = CIG_STATE_INACTIVE;
470469
}
471470

472-
/* Procedure could not be canceled in the current
473-
* state - let it run its course and enqueue a
474-
* terminate procedure.
475-
*/
476-
return ull_cp_cis_terminate(conn, cis, reason);
471+
/* This is now a successful disconnection */
472+
return BT_HCI_ERR_SUCCESS;
477473
}
474+
475+
/* Procedure could not be canceled in the current
476+
* state - let it run its course and enqueue a
477+
* terminate procedure.
478+
*/
479+
return ull_cp_cis_terminate(conn, cis, reason);
478480
}
479481
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */
480482
/* Disallow if CIS is not connected */
@@ -1897,8 +1899,54 @@ static void conn_cleanup_finalize(struct ll_conn *conn)
18971899
struct lll_conn *lll = &conn->lll;
18981900
uint32_t ticker_status;
18991901

1902+
if ((IS_ENABLED(CONFIG_BT_CTLR_PERIPHERAL_ISO) ||
1903+
IS_ENABLED(CONFIG_BT_CTLR_CENTRAL_ISO)) &&
1904+
ull_cp_cc_is_active(conn)) {
1905+
/* CIS creation procedure active, notify LLCP */
1906+
ull_cp_cc_acl_disconnect(conn);
1907+
}
1908+
1909+
/* Set LLCP state - flushes control procedures */
19001910
ull_cp_state_set(conn, ULL_CP_DISCONNECTED);
19011911

1912+
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) || defined(CONFIG_BT_CTLR_CENTRAL_ISO)
1913+
/* Find and clean non-established, allocated CISes associated with this ACL conn */
1914+
for (uint8_t handle = LL_CIS_HANDLE_BASE; handle <= LL_CIS_HANDLE_LAST; handle++) {
1915+
struct ll_conn_iso_stream *cis;
1916+
struct ll_conn_iso_group *cig;
1917+
1918+
cis = ll_conn_iso_stream_get(handle);
1919+
cig = cis->group;
1920+
if (cig != NULL && cig->lll.num_cis != 0U &&
1921+
cis->lll.acl_handle == conn->lll.handle) {
1922+
if (IS_PERIPHERAL(cig)) {
1923+
/* Remove data paths associated with this CIS for both directions.
1924+
* Disable them one at a time to make sure both are removed, even if
1925+
* only one is set.
1926+
* Note: This only applies for peripherals, for centrals an explicit
1927+
* command is required to clean up a CIG and associated CISes.
1928+
* See e.g. BT Core v5.4, Vol 4, Part E, Section 7.8.100
1929+
*/
1930+
ll_remove_iso_path(handle, BIT(BT_HCI_DATAPATH_DIR_HOST_TO_CTLR));
1931+
ll_remove_iso_path(handle, BIT(BT_HCI_DATAPATH_DIR_CTLR_TO_HOST));
1932+
1933+
ll_conn_iso_stream_release(cis);
1934+
cig->lll.num_cis--;
1935+
1936+
if (cig->lll.num_cis == 0U) {
1937+
/* No more streams in the group - release group */
1938+
ll_conn_iso_group_release(cig);
1939+
}
1940+
} else if (IS_CENTRAL(cig) && cig->state == CIG_STATE_INITIATING &&
1941+
ull_central_iso_all_cises_terminated(cig)) {
1942+
1943+
/* We're no longer initiating a connection */
1944+
cig->state = CIG_STATE_INACTIVE;
1945+
}
1946+
}
1947+
}
1948+
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO || CONFIG_BT_CTLR_CENTRAL_ISO */
1949+
19021950
/* Update tx buffer queue handling */
19031951
#if defined(LLCP_TX_CTRL_BUF_QUEUE_ENABLE)
19041952
ull_cp_update_tx_buffer_queue(conn);

subsys/bluetooth/controller/ll_sw/ull_conn_iso.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ struct ll_conn_iso_stream *ll_conn_iso_stream_acquire(void)
136136

137137
if (cis) {
138138
(void)memset(&cis->hdr, 0U, sizeof(cis->hdr));
139+
140+
cis->node_rx_terminate.rx.hdr.link = ll_rx_link_alloc();
141+
/* Link allocation should never fail */
142+
LL_ASSERT_DBG(cis->node_rx_terminate.rx.hdr.link != NULL);
139143
}
140144

141145
return cis;
@@ -146,6 +150,13 @@ void ll_conn_iso_stream_release(struct ll_conn_iso_stream *cis)
146150
cis->cis_id = 0;
147151
cis->group = NULL;
148152

153+
if (cis->node_rx_terminate.rx.hdr.type == NODE_RX_TYPE_NONE &&
154+
cis->node_rx_terminate.rx.hdr.link != NULL) {
155+
/* Free unused link */
156+
ll_rx_link_release(cis->node_rx_terminate.rx.hdr.link);
157+
cis->node_rx_terminate.rx.hdr.link = NULL;
158+
}
159+
149160
mem_release(cis, &cis_free);
150161
}
151162

@@ -1116,6 +1127,27 @@ void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle,
11161127
cis->lll.active = 1U;
11171128
}
11181129

1130+
void ull_conn_iso_cis_terminate_done(struct node_rx_pdu *rx)
1131+
{
1132+
DECLARE_MAYFLY_ARRAY(mfys, cis_disabled_cb, CONFIG_BT_CTLR_CONN_ISO_GROUPS);
1133+
struct ll_conn_iso_stream *cis;
1134+
1135+
cis = ll_conn_iso_stream_get(rx->hdr.handle);
1136+
LL_ASSERT_DBG(rx == &cis->node_rx_terminate.rx);
1137+
1138+
rx->hdr.link = NULL;
1139+
rx->hdr.type = NODE_RX_TYPE_NONE;
1140+
1141+
if (cis->lll.flush != LLL_CIS_FLUSH_NONE) {
1142+
struct ll_conn_iso_group *cig = cis->group;
1143+
1144+
/* Complete teardown of CIS in ULL_HIGH context */
1145+
mfys[cig->lll.handle].param = &cig->lll;
1146+
(void)mayfly_enqueue(TICKER_USER_ID_THREAD,
1147+
TICKER_USER_ID_ULL_HIGH, 1, &mfys[cig->lll.handle]);
1148+
}
1149+
}
1150+
11191151
#if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING)
11201152
static void cis_lazy_fill(struct ll_conn_iso_stream *cis)
11211153
{
@@ -1267,6 +1299,14 @@ static void cis_disabled_cb(void *param)
12671299
} else if (cis->lll.flush == LLL_CIS_FLUSH_COMPLETE) {
12681300
ll_iso_stream_released_cb_t cis_released_cb;
12691301

1302+
if (cis->node_rx_terminate.rx.hdr.type == NODE_RX_TYPE_TERMINATE) {
1303+
/* Termination node type is still set, so it is waiting to
1304+
* be processed. Wait for this to happen before finalizing
1305+
* teardown
1306+
*/
1307+
continue;
1308+
}
1309+
12701310
conn = ll_conn_get(cis->lll.acl_handle);
12711311
LL_ASSERT_DBG(conn != NULL);
12721312

@@ -1323,8 +1363,7 @@ static void cis_disabled_cb(void *param)
13231363
/* Create and enqueue termination node. This shall prevent
13241364
* further enqueuing of TX nodes for terminating CIS.
13251365
*/
1326-
node_terminate = ull_pdu_rx_alloc();
1327-
LL_ASSERT_ERR(node_terminate);
1366+
node_terminate = &cis->node_rx_terminate.rx;
13281367
node_terminate->hdr.handle = cis->lll.handle;
13291368
node_terminate->hdr.type = NODE_RX_TYPE_TERMINATE;
13301369
*((uint8_t *)node_terminate->pdu) = cis->terminate_reason;

subsys/bluetooth/controller/ll_sw/ull_conn_iso_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ void ull_conn_iso_start(struct ll_conn *acl, uint16_t cis_handle,
4040
uint32_t ticks_at_expire, uint32_t remainder,
4141
uint16_t instant_latency);
4242
void ull_conn_iso_done(struct node_rx_event_done *done);
43+
void ull_conn_iso_cis_terminate_done(struct node_rx_pdu *rx);
4344
void ull_conn_iso_cis_stop(struct ll_conn_iso_stream *cis,
4445
ll_iso_stream_released_cb_t cis_released_cb,
4546
uint8_t reason);

subsys/bluetooth/controller/ll_sw/ull_conn_iso_types.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ struct ll_conn_iso_stream {
5151
#if defined(CONFIG_BT_CTLR_ISOAL_PSN_IGNORE)
5252
uint64_t pkt_seq_num:39;
5353
#endif /* CONFIG_BT_CTLR_ISOAL_PSN_IGNORE */
54+
55+
/* node rx type with dummy uint8_t to ensure room for terminate
56+
* reason.
57+
* HCI will reference the value using the pdu member of
58+
* struct node_rx_pdu.
59+
*/
60+
struct {
61+
struct node_rx_pdu rx;
62+
/* Dummy declaration to ensure space allocated to hold one pdu byte */
63+
uint8_t dummy_reason;
64+
} node_rx_terminate;
5465
};
5566

5667
struct ll_conn_iso_group {

0 commit comments

Comments
 (0)