Bluetooth: controller: CIS Central fixes

- New calculation of Max_PDU for framed case according to BT Core 5.4
- Fix CIS_Offset calculation for multiple CIS usecase
- Fix resume ticker setup for central

Signed-off-by: Morten Priess <mtpr@oticon.com>
This commit is contained in:
Morten Priess 2022-11-30 09:50:19 +01:00 committed by Carles Cufí
commit 51ba6e9dd3
2 changed files with 83 additions and 46 deletions

View file

@ -44,6 +44,8 @@
#include "hal/debug.h"
#define SDU_MAX_DRIFT_PPM 100
/* Setup cache for CIG commit transaction */
static struct {
struct ll_conn_iso_group group;
@ -167,6 +169,44 @@ uint8_t ll_cis_parameters_test_set(uint8_t cis_id, uint8_t nse,
return BT_HCI_ERR_SUCCESS;
}
static void set_bn_max_pdu(bool framed, uint32_t iso_interval, uint32_t sdu_interval,
uint16_t max_sdu, uint8_t *bn, uint8_t *max_pdu)
{
uint32_t ceil_f_x_max_sdu;
uint16_t max_pdu_bn1;
uint32_t max_drift;
uint32_t ceil_f;
if (framed) {
/* Framed (From ES-18002):
* Max_PDU >= ((ceil(F) x 5 + ceil(F x Max_SDU)) / BN) + 2
* F = (1 + MaxDrift) x ISO_Interval / SDU_Interval
* SegmentationHeader + TimeOffset = 5 bytes
* Continuation header = 2 bytes
* MaxDrift (Max. allowed SDU delivery timing drift) = 100 ppm
*/
max_drift = ceiling_fraction(SDU_MAX_DRIFT_PPM * sdu_interval, 1000000U);
ceil_f = ceiling_fraction(iso_interval + max_drift, sdu_interval);
ceil_f_x_max_sdu = ceiling_fraction(max_sdu * (iso_interval + max_drift),
sdu_interval);
/* Strategy: Keep lowest possible BN.
* TODO: Implement other strategies, possibly as policies.
*/
max_pdu_bn1 = ceil_f * 5 + ceil_f_x_max_sdu;
*bn = ceiling_fraction(max_pdu_bn1, CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE);
*max_pdu = ceiling_fraction(max_pdu_bn1, *bn) + 2;
} else {
/* For unframed, ISO_Interval must be N x SDU_Interval */
LL_ASSERT(iso_interval % sdu_interval == 0);
/* Core 5.3 Vol 6, Part G section 2.1:
* BN >= ceil(Max_SDU/Max_PDU * ISO_Interval/SDU_Interval)
*/
*bn = ceiling_fraction(max_sdu * iso_interval, (*max_pdu) * sdu_interval);
}
}
/* TODO:
* - Drop retransmissions to stay within Max_Transmission_Latency instead of asserting
@ -278,30 +318,29 @@ uint8_t ll_cig_parameters_commit(uint8_t cig_id)
tx = cig->c_sdu_interval && cis->c_max_sdu;
rx = cig->p_sdu_interval && cis->p_max_sdu;
if (cis->framed) {
cis->lll.tx.max_octets =
ceiling_fraction(cis->c_max_sdu * cig->c_sdu_interval,
iso_interval_us);
cis->lll.rx.max_octets =
ceiling_fraction(cis->p_max_sdu * cig->c_sdu_interval,
iso_interval_us);
} else {
/* For unframed, ISO_Interval must be N x SDU_Interval */
LL_ASSERT(iso_interval_us % cig->c_sdu_interval == 0);
/* Use Max_PDU = MIN(<buffer_size>, Max_SDU) as default. May be changed by
* set_bn_max_pdu.
*/
cis->lll.tx.max_octets = MIN(CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE,
cis->c_max_sdu);
cis->lll.rx.max_octets = MIN(251, cis->p_max_sdu);
/* Use Max_PDU = MIN(<buffer_size>, Max_SDU) */
cis->lll.tx.max_octets = MIN(CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE,
cis->c_max_sdu);
cis->lll.rx.max_octets = MIN(251, cis->p_max_sdu);
/* Calculate BN and Max_PDU (framed) for both directions */
if (tx) {
set_bn_max_pdu(cis->framed, iso_interval_us, cig->c_sdu_interval,
cis->c_max_sdu, &cis->lll.tx.burst_number,
&cis->lll.tx.max_octets);
} else {
cis->lll.tx.burst_number = 0;
}
/* BN >= Max_SDU/Max_PDU * ISO_Interval/SDU_Interval */
cis->lll.tx.burst_number = tx ?
ceiling_fraction(cis->c_max_sdu * iso_interval_us,
cis->lll.tx.max_octets * cig->c_sdu_interval) : 0;
cis->lll.rx.burst_number = rx ?
ceiling_fraction(cis->p_max_sdu * iso_interval_us,
cis->lll.rx.max_octets * cig->p_sdu_interval) : 0;
if (rx) {
set_bn_max_pdu(cis->framed, iso_interval_us, cig->p_sdu_interval,
cis->p_max_sdu, &cis->lll.rx.burst_number,
&cis->lll.rx.max_octets);
} else {
cis->lll.rx.burst_number = 0;
}
}
/* Calculate SE_Length */
@ -621,7 +660,6 @@ uint8_t ull_central_iso_setup(uint16_t cis_handle,
int16_t conn_events_since_ref;
uint32_t iso_interval_us;
uint32_t time_since_ref;
uint32_t acl_offset;
c = ll_conn_iso_stream_get_by_group(cig, &handle_iter);
if (c->cis_id != cis->cis_id && c->lll.active) {
@ -630,9 +668,8 @@ uint8_t ull_central_iso_setup(uint16_t cis_handle,
time_since_ref = c->offset + conn_events_since_ref * conn->lll.interval *
CONN_INT_UNIT_US;
iso_interval_us = cig->iso_interval * CONN_INT_UNIT_US;
acl_offset = iso_interval_us - (time_since_ref % iso_interval_us);
cis_offset = acl_offset + (cig->sync_delay - cis->sync_delay);
iso_interval_us = cig->iso_interval * ISO_INT_UNIT_US;
cis_offset = time_since_ref % iso_interval_us;
break;
}
}

View file

@ -442,7 +442,6 @@ void ull_conn_iso_resume_ticker_start(struct lll_event *resume_event,
uint32_t resume_timeout)
{
struct lll_conn_iso_group *cig;
uint32_t ready_delay_us;
uint32_t resume_delay_us;
int32_t resume_offset_us;
uint8_t ticker_id;
@ -458,26 +457,28 @@ void ull_conn_iso_resume_ticker_start(struct lll_event *resume_event,
}
cig->resume_cis = cis_handle;
if (0) {
#if defined(CONFIG_BT_CTLR_PHY)
} else {
struct ll_conn_iso_stream *cis;
struct ll_conn *acl;
cis = ll_conn_iso_stream_get(cis_handle);
acl = ll_conn_get(cis->lll.acl_handle);
ready_delay_us = lll_radio_rx_ready_delay_get(acl->lll.phy_rx, 1);
#else
} else {
ready_delay_us = lll_radio_rx_ready_delay_get(0, 0);
#endif /* CONFIG_BT_CTLR_PHY */
}
resume_delay_us = EVENT_OVERHEAD_START_US;
resume_delay_us += EVENT_TICKER_RES_MARGIN_US;
resume_delay_us += EVENT_JITTER_US;
resume_delay_us += ready_delay_us;
if (cig->role == BT_HCI_ROLE_PERIPHERAL) {
/* Add peripheral specific delay */
resume_delay_us += EVENT_JITTER_US;
if (0) {
#if defined(CONFIG_BT_CTLR_PHY)
} else {
struct ll_conn_iso_stream *cis;
struct ll_conn *acl;
cis = ll_conn_iso_stream_get(cis_handle);
acl = ll_conn_get(cis->lll.acl_handle);
resume_delay_us += lll_radio_rx_ready_delay_get(acl->lll.phy_rx, 1);
#else
} else {
resume_delay_us += lll_radio_rx_ready_delay_get(0, 0);
#endif /* CONFIG_BT_CTLR_PHY */
}
}
resume_offset_us = (int32_t)(resume_timeout - resume_delay_us);
LL_ASSERT(resume_offset_us >= 0);
@ -631,8 +632,7 @@ void ull_conn_iso_ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
#elif !defined(CONFIG_BT_CTLR_CENTRAL_ISO)
mfy.fp = lll_peripheral_iso_prepare;
#else
mfy.fp = (cig->lll.role == BT_HCI_ROLE_PERIPHERAL) ? lll_peripheral_iso_prepare :
lll_central_iso_prepare;
mfy.fp = IS_PERIPHERAL(cig) ? lll_peripheral_iso_prepare : lll_central_iso_prepare;
#endif
if (IS_PERIPHERAL(cig) && cig->sca_update) {