Bluetooth: controller: Implement LE Create BIG Test command

Implement test version of the LE Create BIG command ll_big_test_create
(BT_HCI_OP_LE_CREATE_BIG_TEST) in the controller.

This enables use of CONFIG_BT_ISO_ADVANCED, which supports use of
the following BIG/QoS configuration parameters (otherwise derived):

- ISO_Interval
- NSE
- Max_PDU
- BN
- IRC
- PTO

Signed-off-by: Morten Priess <mtpr@oticon.com>
This commit is contained in:
Morten Priess 2024-04-02 16:13:02 +02:00 committed by Carles Cufí
commit 2f1a254eae

View file

@ -86,11 +86,13 @@ static struct lll_adv_iso_stream
stream_pool[CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT];
static void *stream_free;
uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
uint32_t sdu_interval, uint16_t max_sdu,
uint16_t max_latency, uint8_t rtn, uint8_t phy,
uint8_t packing, uint8_t framing, uint8_t encryption,
uint8_t *bcode)
static uint8_t big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
uint32_t sdu_interval, uint16_t max_sdu,
uint16_t max_latency, uint8_t rtn, uint8_t phy,
uint8_t packing, uint8_t framing, uint8_t encryption,
uint8_t *bcode,
uint16_t iso_interval, uint8_t nse, uint16_t max_pdu,
uint8_t bn, uint8_t irc, uint8_t pto, bool test_config)
{
uint8_t hdr_data[1 + sizeof(uint8_t *)];
struct lll_adv_sync *lll_adv_sync;
@ -118,7 +120,6 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
uint8_t *acad;
uint32_t ret;
uint8_t err;
uint8_t bn;
int res;
adv_iso = adv_iso_get(big_handle);
@ -156,14 +157,6 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
return BT_HCI_ERR_INVALID_PARAM;
}
if (max_latency > 0x0FA0) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (rtn > 0x0F) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (phy > (BT_HCI_LE_EXT_SCAN_PHY_1M |
BT_HCI_LE_EXT_SCAN_PHY_2M |
BT_HCI_LE_EXT_SCAN_PHY_CODED)) {
@ -181,6 +174,45 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
if (encryption > 1U) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (test_config) {
if (!IN_RANGE(iso_interval, 0x0004, 0x0C80)) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (!IN_RANGE(nse, 0x01, 0x1F)) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (!IN_RANGE(max_pdu, 0x01, MIN(0xFB, LL_BIS_OCTETS_TX_MAX))) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (!IN_RANGE(bn, 0x01, 0x07)) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (!IN_RANGE(irc, 0x01, 0x07)) {
return BT_HCI_ERR_INVALID_PARAM;
}
/* FIXME: PTO is currently limited to BN */
if (!IN_RANGE(pto, 0x00, bn /*0x0F*/)) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (bn * irc + pto < nse) {
return BT_HCI_ERR_INVALID_PARAM;
}
} else {
if (max_latency > 0x0FA0) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (rtn > 0x0F) {
return BT_HCI_ERR_INVALID_PARAM;
}
}
}
/* Check if free BISes available */
@ -276,7 +308,6 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
/* TODO: Move parameters to ULL if only accessed by ULL */
lll_adv_iso = &adv_iso->lll;
lll_adv_iso->handle = big_handle;
lll_adv_iso->max_pdu = MIN(LL_BIS_OCTETS_TX_MAX, max_sdu);
lll_adv_iso->phy = phy;
lll_adv_iso->phy_flags = PHY_FLAGS_S8;
@ -304,31 +335,43 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
adv_iso_stream_handle_get(stream);
}
/* FIXME: SDU per max latency */
sdu_per_event = MAX((max_latency * USEC_PER_MSEC / sdu_interval), 2U) -
1U;
/* BN (Burst Count), Mandatory BN = 1 */
bn = DIV_ROUND_UP(max_sdu, lll_adv_iso->max_pdu) * sdu_per_event;
if (bn > PDU_BIG_BN_MAX) {
/* Restrict each BIG event to maximum burst per BIG event */
lll_adv_iso->bn = PDU_BIG_BN_MAX;
/* Ceil the required burst count per SDU to next maximum burst
* per BIG event.
*/
bn = DIV_ROUND_UP(bn, PDU_BIG_BN_MAX) * PDU_BIG_BN_MAX;
} else {
if (test_config) {
lll_adv_iso->bn = bn;
}
lll_adv_iso->iso_interval = iso_interval;
lll_adv_iso->irc = irc;
lll_adv_iso->nse = nse;
lll_adv_iso->max_pdu = max_pdu;
iso_interval_us = iso_interval * PERIODIC_INT_UNIT_US;
/* Calculate ISO interval */
/* iso_interval shall be at least SDU interval,
* or integer multiple of SDU interval for unframed PDUs
*/
iso_interval_us = ((sdu_interval * lll_adv_iso->bn * sdu_per_event) /
(bn * PERIODIC_INT_UNIT_US)) * PERIODIC_INT_UNIT_US;
lll_adv_iso->iso_interval = iso_interval_us / PERIODIC_INT_UNIT_US;
} else {
lll_adv_iso->max_pdu = MIN(LL_BIS_OCTETS_TX_MAX, max_sdu);
/* FIXME: SDU per max latency */
sdu_per_event = MAX((max_latency * USEC_PER_MSEC / sdu_interval), 2U) -
1U;
/* BN (Burst Count), Mandatory BN = 1 */
bn = DIV_ROUND_UP(max_sdu, lll_adv_iso->max_pdu) * sdu_per_event;
if (bn > PDU_BIG_BN_MAX) {
/* Restrict each BIG event to maximum burst per BIG event */
lll_adv_iso->bn = PDU_BIG_BN_MAX;
/* Ceil the required burst count per SDU to next maximum burst
* per BIG event.
*/
bn = DIV_ROUND_UP(bn, PDU_BIG_BN_MAX) * PDU_BIG_BN_MAX;
} else {
lll_adv_iso->bn = bn;
}
/* Calculate ISO interval */
/* iso_interval shall be at least SDU interval,
* or integer multiple of SDU interval for unframed PDUs
*/
iso_interval_us = ((sdu_interval * lll_adv_iso->bn * sdu_per_event) /
(bn * PERIODIC_INT_UNIT_US)) * PERIODIC_INT_UNIT_US;
lll_adv_iso->iso_interval = iso_interval_us / PERIODIC_INT_UNIT_US;
}
/* Calculate max available ISO event spacing */
slot_overhead = HAL_TICKER_TICKS_TO_US(ticks_slot_overhead);
@ -338,42 +381,50 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
event_spacing_max = 0U;
}
ll_big_create_rtn_retry:
/* Immediate Repetition Count (IRC), Mandatory IRC = 1 */
lll_adv_iso->irc = rtn + 1U;
/* Negotiate event spacing */
do {
if (!test_config) {
/* Immediate Repetition Count (IRC), Mandatory IRC = 1 */
lll_adv_iso->irc = rtn + 1U;
/* Calculate NSE (No. of Sub Events), Mandatory NSE = 1,
* without PTO added.
*/
lll_adv_iso->nse = lll_adv_iso->bn * lll_adv_iso->irc;
/* Calculate NSE (No. of Sub Events), Mandatory NSE = 1,
* without PTO added.
*/
lll_adv_iso->nse = lll_adv_iso->bn * lll_adv_iso->irc;
}
/* NOTE: Calculate sub_interval, if interleaved then it is Num_BIS x
* BIS_Spacing (by BT Spec.)
* else if sequential, then by our implementation, lets keep it
* max_tx_time for Max_PDU + tMSS.
*/
lll_adv_iso->sub_interval = PDU_BIS_US(lll_adv_iso->max_pdu, encryption,
phy, lll_adv_iso->phy_flags) +
EVENT_MSS_US;
ctrl_spacing = PDU_BIS_US(sizeof(struct pdu_big_ctrl), encryption, phy,
lll_adv_iso->phy_flags);
latency_packing = lll_adv_iso->sub_interval * lll_adv_iso->nse *
lll_adv_iso->num_bis;
event_spacing = latency_packing + ctrl_spacing +
EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US;
/* NOTE: Calculate sub_interval, if interleaved then it is Num_BIS x
* BIS_Spacing (by BT Spec.)
* else if sequential, then by our implementation, lets keep it
* max_tx_time for Max_PDU + tMSS.
*/
lll_adv_iso->sub_interval = PDU_BIS_US(lll_adv_iso->max_pdu, encryption,
phy, lll_adv_iso->phy_flags) +
EVENT_MSS_US;
ctrl_spacing = PDU_BIS_US(sizeof(struct pdu_big_ctrl), encryption, phy,
lll_adv_iso->phy_flags);
latency_packing = lll_adv_iso->sub_interval * lll_adv_iso->nse *
lll_adv_iso->num_bis;
event_spacing = latency_packing + ctrl_spacing +
EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US;
/* Check if ISO interval too small to fit the calculated BIG event
* timing required for the supplied BIG create parameters.
*/
if (event_spacing > event_spacing_max) {
/* Check if we can reduce RTN to meet eventing spacing */
if (!test_config && rtn) {
rtn--;
} else {
break;
}
}
} while (event_spacing > event_spacing_max);
/* Check if ISO interval too small to fit the calculated BIG event
* timing required for the supplied BIG create parameters.
*/
if (event_spacing > event_spacing_max) {
/* Check if we can reduce RTN to meet eventing spacing */
if (rtn) {
rtn--;
goto ll_big_create_rtn_retry;
}
/* Release allocated link buffers */
ll_rx_link_release(link_cmplt);
ll_rx_link_release(link_term);
@ -381,29 +432,34 @@ ll_big_create_rtn_retry:
return BT_HCI_ERR_INVALID_PARAM;
}
if (test_config) {
lll_adv_iso->pto = pto;
} else {
lll_adv_iso->ptc = ptc_calc(lll_adv_iso, event_spacing,
event_spacing_max);
/* Pre-Transmission Offset (PTO) */
if (lll_adv_iso->ptc) {
lll_adv_iso->pto = bn / lll_adv_iso->bn;
} else {
lll_adv_iso->pto = 0U;
}
/* Make room for pre-transmissions */
lll_adv_iso->nse += lll_adv_iso->ptc;
}
/* Based on packing requested, sequential or interleaved */
if (packing) {
/* Interleaved Packing */
lll_adv_iso->bis_spacing = lll_adv_iso->sub_interval;
lll_adv_iso->ptc = ptc_calc(lll_adv_iso, event_spacing,
event_spacing_max);
lll_adv_iso->nse += lll_adv_iso->ptc;
lll_adv_iso->sub_interval = lll_adv_iso->bis_spacing *
lll_adv_iso->nse;
lll_adv_iso->nse;
} else {
/* Sequential Packing */
lll_adv_iso->ptc = ptc_calc(lll_adv_iso, event_spacing,
event_spacing_max);
lll_adv_iso->nse += lll_adv_iso->ptc;
lll_adv_iso->bis_spacing = lll_adv_iso->sub_interval *
lll_adv_iso->nse;
}
/* Pre-Transmission Offset (PTO) */
if (lll_adv_iso->ptc) {
lll_adv_iso->pto = bn / lll_adv_iso->bn;
} else {
lll_adv_iso->pto = 0U;
lll_adv_iso->nse;
}
/* TODO: Group count, GC = NSE / BN; PTO = GC - IRC;
@ -588,6 +644,23 @@ ll_big_create_rtn_retry:
return BT_HCI_ERR_SUCCESS;
}
uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
uint32_t sdu_interval, uint16_t max_sdu,
uint16_t max_latency, uint8_t rtn, uint8_t phy,
uint8_t packing, uint8_t framing, uint8_t encryption,
uint8_t *bcode)
{
return big_create(big_handle, adv_handle, num_bis, sdu_interval, max_sdu,
max_latency, rtn, phy, packing, framing, encryption, bcode,
0 /*iso_interval*/,
0 /*nse*/,
0 /*max_pdu*/,
0 /*bn*/,
0 /*irc*/,
0 /*pto*/,
false);
}
uint8_t ll_big_test_create(uint8_t big_handle, uint8_t adv_handle,
uint8_t num_bis, uint32_t sdu_interval,
uint16_t iso_interval, uint8_t nse, uint16_t max_sdu,
@ -595,25 +668,11 @@ uint8_t ll_big_test_create(uint8_t big_handle, uint8_t adv_handle,
uint8_t framing, uint8_t bn, uint8_t irc,
uint8_t pto, uint8_t encryption, uint8_t *bcode)
{
/* TODO: Implement */
ARG_UNUSED(big_handle);
ARG_UNUSED(adv_handle);
ARG_UNUSED(num_bis);
ARG_UNUSED(sdu_interval);
ARG_UNUSED(iso_interval);
ARG_UNUSED(nse);
ARG_UNUSED(max_sdu);
ARG_UNUSED(max_pdu);
ARG_UNUSED(phy);
ARG_UNUSED(packing);
ARG_UNUSED(framing);
ARG_UNUSED(bn);
ARG_UNUSED(irc);
ARG_UNUSED(pto);
ARG_UNUSED(encryption);
ARG_UNUSED(bcode);
return BT_HCI_ERR_CMD_DISALLOWED;
return big_create(big_handle, adv_handle, num_bis, sdu_interval, max_sdu,
0 /*max_latency*/,
0 /*rtn*/,
phy, packing, framing, encryption, bcode,
iso_interval, nse, max_pdu, bn, irc, pto, true);
}
uint8_t ll_big_terminate(uint8_t big_handle, uint8_t reason)