Bluetooth: controller: CIS Central fixes from EBQ run

Various fixes for issues found during EBQ run.

Signed-off-by: Morten Priess <mtpr@oticon.com>
This commit is contained in:
Morten Priess 2022-11-14 14:40:18 +01:00 committed by Carles Cufí
commit afd14d6573
8 changed files with 113 additions and 42 deletions

View file

@ -212,7 +212,7 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id)
if (!cig->central.test) { if (!cig->central.test) {
/* TODO: Calculate ISO_Interval based on SDU_Interval and Max_SDU vs Max_PDU, /* TODO: Calculate ISO_Interval based on SDU_Interval and Max_SDU vs Max_PDU,
* taking the policy into consideration. It may also be intersting to select an * taking the policy into consideration. It may also be interesting to select an
* ISO_Interval which is less likely to collide with other connections. * ISO_Interval which is less likely to collide with other connections.
* For instance: * For instance:
* *
@ -250,9 +250,8 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id)
for (uint8_t i = 0; i < cis_count; i++) { for (uint8_t i = 0; i < cis_count; i++) {
uint32_t mpt_c; uint32_t mpt_c;
uint32_t mpt_p; uint32_t mpt_p;
bool tx;
const bool tx = cig->c_sdu_interval > 0; bool rx;
const bool rx = cig->p_sdu_interval > 0;
/* Acquire new CIS */ /* Acquire new CIS */
cis = ll_conn_iso_stream_acquire(); cis = ll_conn_iso_stream_acquire();
@ -263,16 +262,23 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id)
/* Transfer parameters from update cache */ /* Transfer parameters from update cache */
memcpy(cis, &ll_iso_setup.stream[i], sizeof(struct ll_conn_iso_stream)); memcpy(cis, &ll_iso_setup.stream[i], sizeof(struct ll_conn_iso_stream));
cis->group = cig; cis->group = cig;
cis->framed = cig->central.framing;
cis->lll.handle = ll_conn_iso_stream_handle_get(cis); cis->lll.handle = ll_conn_iso_stream_handle_get(cis);
if (cig->central.test) { if (cig->central.test) {
cis->lll.tx.flush_timeout = ll_iso_setup.c_ft; cis->lll.tx.flush_timeout = ll_iso_setup.c_ft;
cis->lll.rx.flush_timeout = ll_iso_setup.p_ft; cis->lll.rx.flush_timeout = ll_iso_setup.p_ft;
tx = cis->lll.tx.burst_number && cis->lll.tx.max_octets;
rx = cis->lll.rx.burst_number && cis->lll.rx.max_octets;
} else { } else {
LL_ASSERT(iso_interval_us >= cig->c_sdu_interval); LL_ASSERT(iso_interval_us >= cig->c_sdu_interval);
tx = cig->c_sdu_interval && cis->c_max_sdu;
rx = cig->p_sdu_interval && cis->p_max_sdu;
if (cis->framed) { if (cis->framed) {
cis->lll.tx.max_octets = cis->lll.tx.max_octets =
ceiling_fraction(cis->c_max_sdu * cig->c_sdu_interval, ceiling_fraction(cis->c_max_sdu * cig->c_sdu_interval,
@ -300,10 +306,8 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id)
} }
/* Calculate SE_Length */ /* Calculate SE_Length */
mpt_c = PDU_CIS_MAX_US(tx ? cis->lll.tx.max_octets : 0, tx ? 1 : 0, mpt_c = PDU_CIS_MAX_US(cis->lll.tx.max_octets, tx ? 1 : 0, cis->lll.tx.phy);
cis->lll.tx.phy); mpt_p = PDU_CIS_MAX_US(cis->lll.rx.max_octets, rx ? 1 : 0, cis->lll.rx.phy);
mpt_p = PDU_CIS_MAX_US(rx ? cis->lll.rx.max_octets : 0, rx ? 1 : 0,
cis->lll.rx.phy);
se[i].length = mpt_c + EVENT_IFS_US + mpt_p + EVENT_MSS_US; se[i].length = mpt_c + EVENT_IFS_US + mpt_p + EVENT_MSS_US;
max_se_length = MAX(max_se_length, se[i].length); max_se_length = MAX(max_se_length, se[i].length);
@ -366,10 +370,28 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id)
cis->lll.rx.flush_timeout = cis->lll.tx.flush_timeout; cis->lll.rx.flush_timeout = cis->lll.tx.flush_timeout;
#elif defined(CONFIG_BT_CTLR_CONN_ISO_RELIABILITY_POLICY) #elif defined(CONFIG_BT_CTLR_CONN_ISO_RELIABILITY_POLICY)
cis->lll.tx.flush_timeout = ceiling_fraction(cig->c_latency, /* Utilize Max_Transmission_latency */
iso_interval_us); if (cis->framed) {
cis->lll.rx.flush_timeout = ceiling_fraction(cig->p_latency, /* TL = CIG_Sync_Delay + FT x ISO_Interval + SDU_Interval.
iso_interval_us); * SDU_Interval <= CIG_Sync_Delay
*/
cis->lll.tx.flush_timeout =
ceiling_fraction(cig->c_latency - cig->c_sdu_interval -
iso_interval_us, iso_interval_us);
cis->lll.rx.flush_timeout =
ceiling_fraction(cig->p_latency - cig->p_sdu_interval -
iso_interval_us, iso_interval_us);
} else {
/* TL = CIG_Sync_Delay + FT x ISO_Interval - SDU_Interval.
* SDU_Interval <= CIG_Sync_Delay
*/
cis->lll.tx.flush_timeout =
ceiling_fraction(cig->c_latency + cig->c_sdu_interval -
iso_interval_us, iso_interval_us);
cis->lll.rx.flush_timeout =
ceiling_fraction(cig->p_latency + cig->p_sdu_interval -
iso_interval_us, iso_interval_us);
}
#else #else
LL_ASSERT(0); LL_ASSERT(0);
#endif #endif
@ -459,11 +481,20 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id)
return BT_HCI_ERR_SUCCESS; return BT_HCI_ERR_SUCCESS;
} }
/* Core 5.3 Vol 6, Part B section 7.8.100:
* The HCI_LE_Remove_CIG command is used by the Centrals Host to remove the CIG
* identified by CIG_ID.
* This command shall delete the CIG_ID and also delete the Connection_Handles
* of the CIS configurations stored in the CIG.
* This command shall also remove the isochronous data paths that are associated
* with the Connection_Handles of the CIS configurations.
*/
uint8_t ll_cig_remove(uint8_t cig_id) uint8_t ll_cig_remove(uint8_t cig_id)
{ {
struct ll_conn_iso_stream *cis; struct ll_conn_iso_stream *cis;
struct ll_conn_iso_group *cig; struct ll_conn_iso_group *cig;
uint16_t handle_iter; uint16_t handle_iter;
bool has_cis;
cig = ll_conn_iso_group_get_by_id(cig_id); cig = ll_conn_iso_group_get_by_id(cig_id);
if (!cig) { if (!cig) {
@ -481,6 +512,10 @@ uint8_t ll_cig_remove(uint8_t cig_id)
struct ll_conn *conn; struct ll_conn *conn;
cis = ll_conn_iso_stream_get_by_group(cig, &handle_iter); cis = ll_conn_iso_stream_get_by_group(cig, &handle_iter);
if (!cis) {
break;
}
conn = ll_connected_get(cis->lll.acl_handle); conn = ll_connected_get(cis->lll.acl_handle);
if (conn) { if (conn) {
@ -493,8 +528,13 @@ uint8_t ll_cig_remove(uint8_t cig_id)
/* CIG exists and is not active */ /* CIG exists and is not active */
handle_iter = UINT16_MAX; handle_iter = UINT16_MAX;
has_cis = false;
for (int i = 0; i < cig->cis_count; i++) { for (int i = 0; i < cig->cis_count; i++) {
cis = ll_conn_iso_stream_get_by_group(cig, &handle_iter); cis = ll_conn_iso_stream_get_by_group(cig, &handle_iter);
if (!cis) {
break;
}
/* Remove data path and ISOAL sink/source associated with this CIS /* Remove data path and ISOAL sink/source associated with this CIS
* for both directions. * for both directions.
@ -502,10 +542,16 @@ uint8_t ll_cig_remove(uint8_t cig_id)
ll_remove_iso_path(cis->lll.handle, BT_HCI_DATAPATH_DIR_CTLR_TO_HOST); ll_remove_iso_path(cis->lll.handle, BT_HCI_DATAPATH_DIR_CTLR_TO_HOST);
ll_remove_iso_path(cis->lll.handle, BT_HCI_DATAPATH_DIR_HOST_TO_CTLR); ll_remove_iso_path(cis->lll.handle, BT_HCI_DATAPATH_DIR_HOST_TO_CTLR);
ll_conn_iso_stream_release(cis); has_cis = true;
} }
ll_conn_iso_group_release(cig); if (has_cis) {
/* Clear configuration only - let CIS disconnection release instance */
cig->cis_count = 0;
} else {
/* No CISes associated with the CIG - release the instance */
ll_conn_iso_group_release(cig);
}
return BT_HCI_ERR_SUCCESS; return BT_HCI_ERR_SUCCESS;
} }
@ -611,22 +657,29 @@ uint8_t ull_central_iso_setup(uint16_t cis_handle,
return 0; return 0;
} }
uint16_t ull_central_iso_cis_offset_get(struct ll_conn_iso_stream *cis, uint32_t *cis_offset_min, uint16_t ull_central_iso_cis_offset_get(uint16_t cis_handle, uint32_t *cis_offset_min,
uint32_t *cis_offset_max) uint32_t *cis_offset_max)
{ {
struct ll_conn_iso_group *cig; struct ll_conn_iso_stream *cis;
struct ll_conn *conn; struct ll_conn *conn;
conn = ll_conn_get(cis->lll.acl_handle); cis = ll_conn_iso_stream_get(cis_handle);
cig = cis->group; LL_ASSERT(cis);
/* Provide CIS offset range conn = ll_conn_get(cis->lll.acl_handle);
* CIS_Offset_Max < (connInterval - (CIG_Sync_Delay + T_MSS))
*/ if (cis_offset_min && cis_offset_max) {
*cis_offset_max = (conn->lll.interval * CONN_INT_UNIT_US) - cig->sync_delay; struct ll_conn_iso_group *cig;
*cis_offset_min = MAX(400, EVENT_OVERHEAD_CIS_SETUP_US);
cig = cis->group;
/* Provide CIS offset range
* CIS_Offset_Max < (connInterval - (CIG_Sync_Delay + T_MSS))
*/
*cis_offset_max = (conn->lll.interval * CONN_INT_UNIT_US) - cig->sync_delay;
*cis_offset_min = MAX(400, EVENT_OVERHEAD_CIS_SETUP_US);
}
cis->central.instant = ull_conn_event_counter(conn) + 3; cis->central.instant = ull_conn_event_counter(conn) + 3;
return cis->central.instant; return cis->central.instant;
} }

View file

@ -8,8 +8,7 @@
int ull_central_iso_init(void); int ull_central_iso_init(void);
int ull_central_iso_reset(void); int ull_central_iso_reset(void);
void ull_central_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire, uint16_t cis_handle); uint16_t ull_central_iso_cis_offset_get(uint16_t cis_handle, uint32_t *cis_offset_min,
uint16_t ull_central_iso_cis_offset_get(struct ll_conn_iso_stream *cis, uint32_t *cis_offset_min,
uint32_t *cis_offset_max); uint32_t *cis_offset_max);
uint8_t ull_central_iso_setup(uint16_t cis_handle, uint8_t ull_central_iso_setup(uint16_t cis_handle,
uint32_t *cig_sync_delay, uint32_t *cig_sync_delay,

View file

@ -520,7 +520,7 @@ void ull_conn_iso_ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
/* New CIS may become available by creation prior to the CIG /* New CIS may become available by creation prior to the CIG
* event in which it has event_count == 0. Don't increment * event in which it has event_count == 0. Don't increment
* event count until its handle is validated in * event count until its handle is validated in
* ull_peripheral_iso_start, which means that its ACL instant * ull_conn_iso_start, which means that its ACL instant
* has been reached, and offset calculated. * has been reached, and offset calculated.
*/ */
if (cis->lll.handle != 0xFFFF && cis->lll.active) { if (cis->lll.handle != 0xFFFF && cis->lll.active) {
@ -538,7 +538,7 @@ void ull_conn_iso_ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
/* Update the CIG reference point for this event. Event 0 for the /* Update the CIG reference point for this event. Event 0 for the
* leading CIS in the CIG would have had it's reference point set in * leading CIS in the CIG would have had it's reference point set in
* ull_peripheral_iso_start(). The reference point should only be * ull_conn_iso_start(). The reference point should only be
* updated from event 1 onwards. Although the cig reference point set * updated from event 1 onwards. Although the cig reference point set
* this way is not accurate, it is the best possible until the anchor * this way is not accurate, it is the best possible until the anchor
* point for the leading CIS is available for this event. * point for the leading CIS is available for this event.
@ -952,7 +952,7 @@ static void cig_disabled_cb(void *param)
cig = HDR_LLL2ULL(param); cig = HDR_LLL2ULL(param);
if (IS_PERIPHERAL(cig)) { if (IS_PERIPHERAL(cig) || cig->cis_count == 0) {
ll_conn_iso_group_release(cig); ll_conn_iso_group_release(cig);
} else { } else {
/* CIG shall be released by ll_cig_remove */ /* CIG shall be released by ll_cig_remove */

View file

@ -905,8 +905,9 @@ uint8_t ull_cp_cis_create(struct ll_conn *conn, struct ll_conn_iso_stream *cis)
ctx->data.cis_create.p_ft = cis->lll.rx.flush_timeout; ctx->data.cis_create.p_ft = cis->lll.rx.flush_timeout;
ctx->data.cis_create.conn_event_count = ctx->data.cis_create.conn_event_count =
ull_central_iso_cis_offset_get(cis, &ctx->data.cis_create.cis_offset_min, ull_central_iso_cis_offset_get(cis->lll.handle,
&ctx->data.cis_create.cis_offset_max); &ctx->data.cis_create.cis_offset_min,
&ctx->data.cis_create.cis_offset_max);
llcp_lr_enqueue(conn, ctx); llcp_lr_enqueue(conn, ctx);

View file

@ -601,7 +601,7 @@ void llcp_rp_cc_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
} }
#endif /* CONFIG_BT_PERIPHERAL */ #endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CENTRAL) #if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
static void lp_cc_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param); static void lp_cc_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param);
/* LLCP Local Procedure FSM states */ /* LLCP Local Procedure FSM states */
@ -685,6 +685,10 @@ static void lp_cc_send_cis_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8
if (llcp_lr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { if (llcp_lr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = LP_CC_STATE_WAIT_TX_CIS_REQ; ctx->state = LP_CC_STATE_WAIT_TX_CIS_REQ;
} else { } else {
/* Update conn_event_count */
ctx->data.cis_create.conn_event_count =
ull_central_iso_cis_offset_get(ctx->data.cis_create.cis_handle, NULL, NULL);
lp_cc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CIS_REQ); lp_cc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CIS_REQ);
ctx->state = LP_CC_STATE_WAIT_RX_CIS_RSP; ctx->state = LP_CC_STATE_WAIT_RX_CIS_RSP;
@ -821,9 +825,6 @@ static void lp_cc_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint
/* Now we can wait for CIS to become established */ /* Now we can wait for CIS to become established */
ctx->state = LP_CC_STATE_WAIT_ESTABLISHED; ctx->state = LP_CC_STATE_WAIT_ESTABLISHED;
/* Stop procedure response timeout timer */
llcp_lr_prt_stop(conn);
} }
} }
@ -908,4 +909,4 @@ bool llcp_lp_cc_is_active(struct proc_ctx *ctx)
{ {
return ctx->state != LP_CC_STATE_IDLE; return ctx->state != LP_CC_STATE_IDLE;
} }
#endif /* CONFIG_BT_CENTRAL */ #endif /* CONFIG_BT_CTLR_CENTRAL_ISO */

View file

@ -37,3 +37,20 @@ int ull_central_reset(void)
{ {
return 0; return 0;
} }
uint16_t ull_central_iso_cis_offset_get(uint16_t cis_handle, uint32_t *cis_offset_min,
uint32_t *cis_offset_max)
{
return 0;
}
uint8_t ull_central_iso_setup(uint16_t cis_handle,
uint32_t *cig_sync_delay,
uint32_t *cis_sync_delay,
uint32_t *cis_offset_min,
uint32_t *cis_offset_max,
uint16_t *conn_event_count,
uint8_t *access_addr)
{
return 0;
}

View file

@ -77,3 +77,8 @@ void ull_conn_iso_cis_stop_by_id(uint8_t cig_id, uint8_t cis_id, uint8_t reason)
{ {
} }
void ull_conn_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire, uint16_t cis_handle)
{
}

View file

@ -56,11 +56,6 @@ uint8_t ull_peripheral_iso_setup(struct pdu_data_llctrl_cis_ind *ind,
return 0; return 0;
} }
void ull_peripheral_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire,
uint16_t cis_handle)
{
}
void ull_peripheral_iso_update_peer_sca(struct ll_conn *acl) void ull_peripheral_iso_update_peer_sca(struct ll_conn *acl)
{ {
} }