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:
parent
745bf7297e
commit
afd14d6573
8 changed files with 113 additions and 42 deletions
|
@ -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 Central’s 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue