Several members of the ull_hdr can be removed, saving 12 bytes from all ULL instances used (and a bit of code as well) ticks_active_to_start is always 0 and can be removed completely ticks_prepare_to_start is always set to HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US), so replace usage of it by this constant ticks_preempt_to_start is always set to HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US), so replace usage of it by this constant In addition, remove logic handling usage of XON_BITMASK since it was only used by the long removed legacy LL Signed-off-by: Troels Nilsson <trnn@demant.com>
1735 lines
47 KiB
C
1735 lines
47 KiB
C
/*
|
|
* Copyright (c) 2021 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <soc.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
#include <zephyr/bluetooth/hci_types.h>
|
|
|
|
#include "hal/cpu.h"
|
|
#include "hal/ccm.h"
|
|
#include "hal/ticker.h"
|
|
|
|
#include "util/util.h"
|
|
#include "util/mem.h"
|
|
#include "util/memq.h"
|
|
#include "util/mfifo.h"
|
|
#include "util/mayfly.h"
|
|
|
|
#include "ticker/ticker.h"
|
|
|
|
#include "pdu_df.h"
|
|
#include "lll/pdu_vendor.h"
|
|
#include "pdu.h"
|
|
|
|
#include "lll.h"
|
|
#include "lll/lll_vendor.h"
|
|
#include "lll/lll_adv_types.h"
|
|
#include "lll_adv.h"
|
|
#include "lll/lll_adv_pdu.h"
|
|
#include "lll_adv_iso.h"
|
|
#include "lll_iso_tx.h"
|
|
|
|
#include "isoal.h"
|
|
|
|
#include "ull_adv_types.h"
|
|
#include "ull_iso_types.h"
|
|
|
|
#include "ull_internal.h"
|
|
#include "ull_adv_internal.h"
|
|
#include "ull_chan_internal.h"
|
|
#include "ull_sched_internal.h"
|
|
#include "ull_iso_internal.h"
|
|
|
|
#include "ll.h"
|
|
#include "ll_feat.h"
|
|
|
|
#include "bt_crypto.h"
|
|
|
|
#include "hal/debug.h"
|
|
|
|
/* Controller implementation dependent minimum Pre-Transmission Offset and
|
|
* Pre-Transmission Group Count to use when there is available time space in the
|
|
* BIG events.
|
|
* The number of Pre-Transmission Group Count configure how many future ISO SDUs
|
|
* from the Offset will be Pre-Transmitted in advance in the current BIG event.
|
|
*
|
|
* TODO: These could be a Kconfig option.
|
|
*/
|
|
#define BT_CTLR_ADV_ISO_PTO_MIN 1U
|
|
#define BT_CTLR_ADV_ISO_PTO_GROUP_COUNT 1U
|
|
|
|
static int init_reset(void);
|
|
static struct ll_adv_iso_set *adv_iso_get(uint8_t handle);
|
|
static struct stream *adv_iso_stream_acquire(void);
|
|
static uint16_t adv_iso_stream_handle_get(struct lll_adv_iso_stream *stream);
|
|
static uint8_t ptc_calc(const struct lll_adv_iso *lll, uint32_t event_spacing,
|
|
uint32_t event_spacing_max);
|
|
static uint32_t adv_iso_time_get(const struct ll_adv_iso_set *adv_iso, bool max);
|
|
static uint32_t adv_iso_start(struct ll_adv_iso_set *adv_iso,
|
|
uint32_t iso_interval_us);
|
|
static uint8_t adv_iso_chm_update(uint8_t big_handle);
|
|
static void adv_iso_chm_complete_commit(struct lll_adv_iso *lll_iso);
|
|
static void mfy_iso_offset_get(void *param);
|
|
static void pdu_big_info_chan_map_phy_set(uint8_t *chm_phy, uint8_t *chan_map,
|
|
uint8_t phy);
|
|
static inline struct pdu_big_info *big_info_get(struct pdu_adv *pdu);
|
|
static inline void big_info_offset_fill(struct pdu_big_info *bi,
|
|
uint32_t ticks_offset,
|
|
uint32_t start_us);
|
|
static void ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
|
|
uint32_t remainder, uint16_t lazy, uint8_t force,
|
|
void *param);
|
|
static void ticker_op_cb(uint32_t status, void *param);
|
|
static void ticker_stop_op_cb(uint32_t status, void *param);
|
|
static void adv_iso_disable(void *param);
|
|
static void disabled_cb(void *param);
|
|
static void tx_lll_flush(void *param);
|
|
|
|
static memq_link_t link_lll_prepare;
|
|
static struct mayfly mfy_lll_prepare = {0U, 0U, &link_lll_prepare, NULL, NULL};
|
|
|
|
static struct ll_adv_iso_set ll_adv_iso[CONFIG_BT_CTLR_ADV_ISO_SET];
|
|
static struct lll_adv_iso_stream
|
|
stream_pool[CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT];
|
|
static void *stream_free;
|
|
|
|
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 bi_ad[PDU_BIG_INFO_ENCRYPTED_SIZE + 2U];
|
|
struct lll_adv_sync *lll_adv_sync;
|
|
struct lll_adv_iso *lll_adv_iso;
|
|
struct ll_adv_iso_set *adv_iso;
|
|
struct pdu_adv *pdu_prev, *pdu;
|
|
struct pdu_big_info *big_info;
|
|
uint32_t ticks_slot_overhead;
|
|
struct ll_adv_sync_set *sync;
|
|
struct ll_adv_aux_set *aux;
|
|
uint32_t event_spacing_max;
|
|
uint8_t pdu_big_info_size;
|
|
uint32_t iso_interval_us;
|
|
uint32_t latency_packing;
|
|
uint32_t ticks_slot_sync;
|
|
uint32_t ticks_slot_aux;
|
|
memq_link_t *link_cmplt;
|
|
memq_link_t *link_term;
|
|
struct ll_adv_set *adv;
|
|
uint32_t slot_overhead;
|
|
uint32_t event_spacing;
|
|
uint16_t ctrl_spacing;
|
|
uint8_t sdu_per_event;
|
|
uint8_t ter_idx;
|
|
uint32_t ret;
|
|
uint8_t err;
|
|
int res;
|
|
|
|
adv_iso = adv_iso_get(big_handle);
|
|
|
|
/* Already created */
|
|
if (!adv_iso || adv_iso->lll.adv) {
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
}
|
|
|
|
/* No advertising set created */
|
|
adv = ull_adv_is_created_get(adv_handle);
|
|
if (!adv) {
|
|
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
|
|
}
|
|
|
|
/* Does not identify a periodic advertising train or
|
|
* the periodic advertising trains is already associated
|
|
* with another BIG.
|
|
*/
|
|
lll_adv_sync = adv->lll.sync;
|
|
if (!lll_adv_sync || lll_adv_sync->iso) {
|
|
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
|
|
}
|
|
|
|
/* Check if encryption supported */
|
|
if (!IS_ENABLED(CONFIG_BT_CTLR_BROADCAST_ISO_ENC) &&
|
|
encryption) {
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
};
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK)) {
|
|
if (num_bis == 0U || num_bis > 0x1F) {
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (sdu_interval < 0x000100 || sdu_interval > 0x0FFFFF) {
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (max_sdu < 0x0001 || max_sdu > 0x0FFF) {
|
|
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)) {
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (packing > 1U) {
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (framing > 1U) {
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
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, 0x0F)) {
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (pto > 0x0F) {
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (pto && !(bn * irc < 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 */
|
|
if (mem_free_count_get(stream_free) < num_bis) {
|
|
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
|
|
}
|
|
|
|
/* Allocate link buffer for created event */
|
|
link_cmplt = ll_rx_link_alloc();
|
|
if (!link_cmplt) {
|
|
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
|
|
}
|
|
|
|
/* Allocate link buffer for sync lost event */
|
|
link_term = ll_rx_link_alloc();
|
|
if (!link_term) {
|
|
ll_rx_link_release(link_cmplt);
|
|
|
|
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
|
|
}
|
|
|
|
/* Check if aux context allocated before we are creating ISO */
|
|
if (adv->lll.aux) {
|
|
aux = HDR_LLL2ULL(adv->lll.aux);
|
|
} else {
|
|
aux = NULL;
|
|
}
|
|
|
|
/* Calculate overheads due to extended advertising. */
|
|
if (aux && aux->is_started) {
|
|
ticks_slot_aux = aux->ull.ticks_slot;
|
|
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
|
|
ticks_slot_overhead = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US);
|
|
} else {
|
|
ticks_slot_overhead = 0U;
|
|
}
|
|
} else {
|
|
uint32_t time_us;
|
|
|
|
time_us = PDU_AC_US(PDU_AC_PAYLOAD_SIZE_MAX, adv->lll.phy_s,
|
|
adv->lll.phy_flags) +
|
|
EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US;
|
|
ticks_slot_aux = HAL_TICKER_US_TO_TICKS_CEIL(time_us);
|
|
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
|
|
/* Assume primary overheads may be inherited by aux */
|
|
ticks_slot_overhead = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US);
|
|
} else {
|
|
ticks_slot_overhead = 0U;
|
|
}
|
|
}
|
|
ticks_slot_aux += ticks_slot_overhead;
|
|
|
|
/* Calculate overheads due to periodic advertising. */
|
|
sync = HDR_LLL2ULL(lll_adv_sync);
|
|
if (sync->is_started) {
|
|
ticks_slot_sync = sync->ull.ticks_slot;
|
|
} else {
|
|
uint32_t time_us;
|
|
|
|
time_us = PDU_AC_US(PDU_AC_PAYLOAD_SIZE_MAX,
|
|
sync->lll.adv->phy_s,
|
|
sync->lll.adv->phy_flags) +
|
|
EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US;
|
|
ticks_slot_sync = HAL_TICKER_US_TO_TICKS_CEIL(time_us);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
|
|
ticks_slot_overhead = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US);
|
|
} else {
|
|
ticks_slot_overhead = 0U;
|
|
}
|
|
|
|
ticks_slot_sync += ticks_slot_overhead;
|
|
|
|
/* Calculate total overheads due to extended and periodic advertising */
|
|
if (false) {
|
|
|
|
#if defined(CONFIG_BT_CTLR_ADV_AUX_SYNC_OFFSET)
|
|
} else if (CONFIG_BT_CTLR_ADV_AUX_SYNC_OFFSET > 0U) {
|
|
ticks_slot_overhead = MAX(ticks_slot_aux, ticks_slot_sync);
|
|
#endif /* CONFIG_BT_CTLR_ADV_AUX_SYNC_OFFSET */
|
|
|
|
} else {
|
|
ticks_slot_overhead = ticks_slot_aux + ticks_slot_sync;
|
|
}
|
|
|
|
/* Store parameters in LLL context */
|
|
/* 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->phy = phy;
|
|
lll_adv_iso->phy_flags = PHY_FLAGS_S8;
|
|
|
|
/* Mandatory Num_BIS = 1 */
|
|
lll_adv_iso->num_bis = num_bis;
|
|
|
|
/* Allocate streams */
|
|
for (uint8_t i = 0U; i < num_bis; i++) {
|
|
struct lll_adv_iso_stream *stream;
|
|
|
|
stream = (void *)adv_iso_stream_acquire();
|
|
stream->big_handle = big_handle;
|
|
stream->dp = NULL;
|
|
|
|
if (!stream->link_tx_free) {
|
|
stream->link_tx_free = &stream->link_tx;
|
|
}
|
|
memq_init(stream->link_tx_free, &stream->memq_tx.head,
|
|
&stream->memq_tx.tail);
|
|
stream->link_tx_free = NULL;
|
|
|
|
stream->pkt_seq_num = 0U;
|
|
|
|
lll_adv_iso->stream_handle[i] =
|
|
adv_iso_stream_handle_get(stream);
|
|
}
|
|
|
|
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;
|
|
|
|
} else {
|
|
if (framing) {
|
|
/* Try to allocate room for one SDU + header */
|
|
lll_adv_iso->max_pdu = MIN(LL_BIS_OCTETS_TX_MAX,
|
|
max_sdu + PDU_ISO_SEG_HDR_SIZE +
|
|
PDU_ISO_SEG_TIMEOFFSET_SIZE);
|
|
} else {
|
|
lll_adv_iso->max_pdu = MIN(LL_BIS_OCTETS_TX_MAX, max_sdu);
|
|
}
|
|
|
|
/* FIXME: SDU per max latency, consider how to use Pre-transmission in the
|
|
* calculations.
|
|
* Take decision based on how ptc_calc function forces the use of
|
|
* Pre-Transmission when not using test command. Refer to comments in
|
|
* ptc_calc function.
|
|
*/
|
|
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);
|
|
if (slot_overhead < iso_interval_us) {
|
|
event_spacing_max = iso_interval_us - slot_overhead;
|
|
} else {
|
|
event_spacing_max = 0U;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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) {
|
|
/* Release allocated link buffers */
|
|
ll_rx_link_release(link_cmplt);
|
|
ll_rx_link_release(link_term);
|
|
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
/* Decision to use requested Pre-Transmission Offset or force Pre-Transmission when
|
|
* possible (Zephyr Controller decision).
|
|
*/
|
|
lll_adv_iso->ptc = ptc_calc(lll_adv_iso, event_spacing, event_spacing_max);
|
|
|
|
if (test_config) {
|
|
lll_adv_iso->pto = pto;
|
|
|
|
if (pto && !lll_adv_iso->ptc) {
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
}
|
|
} else {
|
|
/* Pre-Transmission Offset (PTO) */
|
|
if (lll_adv_iso->ptc) {
|
|
lll_adv_iso->pto = MAX((bn / lll_adv_iso->bn), BT_CTLR_ADV_ISO_PTO_MIN);
|
|
} 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 (false) {
|
|
|
|
#if defined(CONFIG_BT_CTLR_ADV_ISO_INTERLEAVED)
|
|
} else if (packing) {
|
|
/* Interleaved Packing */
|
|
lll_adv_iso->bis_spacing = lll_adv_iso->sub_interval;
|
|
lll_adv_iso->sub_interval = lll_adv_iso->bis_spacing *
|
|
lll_adv_iso->num_bis;
|
|
#endif /* CONFIG_BT_CTLR_ADV_ISO_INTERLEAVED */
|
|
|
|
#if defined(CONFIG_BT_CTLR_ADV_ISO_SEQUENTIAL)
|
|
} else if (true) {
|
|
/* Sequential Packing */
|
|
lll_adv_iso->bis_spacing = lll_adv_iso->sub_interval *
|
|
lll_adv_iso->nse;
|
|
#endif /* CONFIG_BT_CTLR_ADV_ISO_SEQUENTIAL */
|
|
|
|
} else {
|
|
return BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL;
|
|
}
|
|
|
|
/* TODO: Group count, GC = NSE / BN; PTO = GC - IRC;
|
|
* Is this required?
|
|
*/
|
|
|
|
lll_adv_iso->sdu_interval = sdu_interval;
|
|
lll_adv_iso->max_sdu = max_sdu;
|
|
|
|
res = util_saa_le32(lll_adv_iso->seed_access_addr, big_handle);
|
|
LL_ASSERT(!res);
|
|
|
|
(void)lll_csrand_get(lll_adv_iso->base_crc_init,
|
|
sizeof(lll_adv_iso->base_crc_init));
|
|
lll_adv_iso->data_chan_count =
|
|
ull_chan_map_get(lll_adv_iso->data_chan_map);
|
|
lll_adv_iso->payload_count = 0U;
|
|
lll_adv_iso->latency_prepare = 0U;
|
|
lll_adv_iso->latency_event = 0U;
|
|
lll_adv_iso->term_req = 0U;
|
|
lll_adv_iso->term_ack = 0U;
|
|
lll_adv_iso->chm_req = 0U;
|
|
lll_adv_iso->chm_ack = 0U;
|
|
lll_adv_iso->ctrl_expire = 0U;
|
|
|
|
/* TODO: framing support */
|
|
lll_adv_iso->framing = framing;
|
|
|
|
/* Allocate next PDU */
|
|
err = ull_adv_sync_pdu_alloc(adv, ULL_ADV_PDU_EXTRA_DATA_ALLOC_IF_EXIST,
|
|
&pdu_prev, &pdu, NULL, NULL, &ter_idx);
|
|
if (err) {
|
|
/* Insufficient Advertising PDU buffers to allocate new PDU
|
|
* to add BIGInfo into the ACAD of the Periodic Advertising.
|
|
*/
|
|
|
|
/* Release allocated link buffers */
|
|
ll_rx_link_release(link_cmplt);
|
|
ll_rx_link_release(link_term);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Add ACAD to AUX_SYNC_IND */
|
|
if (encryption) {
|
|
pdu_big_info_size = PDU_BIG_INFO_ENCRYPTED_SIZE;
|
|
} else {
|
|
pdu_big_info_size = PDU_BIG_INFO_CLEARTEXT_SIZE;
|
|
}
|
|
bi_ad[PDU_ADV_DATA_HEADER_LEN_OFFSET] = pdu_big_info_size + (PDU_ADV_DATA_HEADER_SIZE -
|
|
PDU_ADV_DATA_HEADER_LEN_SIZE);
|
|
bi_ad[PDU_ADV_DATA_HEADER_TYPE_OFFSET] = BT_DATA_BIG_INFO;
|
|
big_info = (void *)&bi_ad[PDU_ADV_DATA_HEADER_DATA_OFFSET];
|
|
|
|
/* big_info->offset, big_info->offset_units and
|
|
* big_info->payload_count_framing[] will be filled by periodic
|
|
* advertising event.
|
|
*/
|
|
|
|
PDU_BIG_INFO_ISO_INTERVAL_SET(big_info, iso_interval_us / PERIODIC_INT_UNIT_US);
|
|
PDU_BIG_INFO_NUM_BIS_SET(big_info, lll_adv_iso->num_bis);
|
|
PDU_BIG_INFO_NSE_SET(big_info, lll_adv_iso->nse);
|
|
PDU_BIG_INFO_BN_SET(big_info, lll_adv_iso->bn);
|
|
PDU_BIG_INFO_SUB_INTERVAL_SET(big_info, lll_adv_iso->sub_interval);
|
|
PDU_BIG_INFO_PTO_SET(big_info, lll_adv_iso->pto);
|
|
PDU_BIG_INFO_SPACING_SET(big_info, lll_adv_iso->bis_spacing);
|
|
PDU_BIG_INFO_IRC_SET(big_info, lll_adv_iso->irc);
|
|
|
|
big_info->max_pdu = lll_adv_iso->max_pdu;
|
|
big_info->rfu = 0U;
|
|
|
|
(void)memcpy(&big_info->seed_access_addr, lll_adv_iso->seed_access_addr,
|
|
sizeof(big_info->seed_access_addr));
|
|
PDU_BIG_INFO_SDU_INTERVAL_SET(big_info, sdu_interval);
|
|
PDU_BIG_INFO_MAX_SDU_SET(big_info, max_sdu);
|
|
(void)memcpy(&big_info->base_crc_init, lll_adv_iso->base_crc_init,
|
|
sizeof(big_info->base_crc_init));
|
|
pdu_big_info_chan_map_phy_set(big_info->chm_phy,
|
|
lll_adv_iso->data_chan_map,
|
|
phy);
|
|
/* Assign the 39-bit payload count, and 1-bit framing */
|
|
big_info->payload_count_framing[0] = lll_adv_iso->payload_count;
|
|
big_info->payload_count_framing[1] = lll_adv_iso->payload_count >> 8;
|
|
big_info->payload_count_framing[2] = lll_adv_iso->payload_count >> 16;
|
|
big_info->payload_count_framing[3] = lll_adv_iso->payload_count >> 24;
|
|
big_info->payload_count_framing[4] = lll_adv_iso->payload_count >> 32;
|
|
big_info->payload_count_framing[4] &= ~BIT(7);
|
|
big_info->payload_count_framing[4] |= ((framing & 0x01) << 7);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CTLR_BROADCAST_ISO_ENC) && encryption) {
|
|
const uint8_t BIG1[16] = {0x31, 0x47, 0x49, 0x42, };
|
|
const uint8_t BIG2[4] = {0x32, 0x47, 0x49, 0x42};
|
|
const uint8_t BIG3[4] = {0x33, 0x47, 0x49, 0x42};
|
|
struct ccm *ccm_tx;
|
|
uint8_t igltk[16];
|
|
uint8_t gltk[16];
|
|
uint8_t gsk[16];
|
|
|
|
/* Fill GIV and GSKD */
|
|
(void)lll_csrand_get(lll_adv_iso->giv,
|
|
sizeof(lll_adv_iso->giv));
|
|
(void)memcpy(big_info->giv, lll_adv_iso->giv,
|
|
sizeof(big_info->giv));
|
|
(void)lll_csrand_get(big_info->gskd, sizeof(big_info->gskd));
|
|
|
|
/* Calculate GSK */
|
|
err = bt_crypto_h7(BIG1, bcode, igltk);
|
|
LL_ASSERT(!err);
|
|
err = bt_crypto_h6(igltk, BIG2, gltk);
|
|
LL_ASSERT(!err);
|
|
err = bt_crypto_h8(gltk, big_info->gskd, BIG3, gsk);
|
|
LL_ASSERT(!err);
|
|
|
|
/* Prepare the CCM parameters */
|
|
ccm_tx = &lll_adv_iso->ccm_tx;
|
|
ccm_tx->direction = 1U;
|
|
(void)memcpy(&ccm_tx->iv[4], &lll_adv_iso->giv[4], 4U);
|
|
(void)mem_rcopy(ccm_tx->key, gsk, sizeof(ccm_tx->key));
|
|
|
|
/* NOTE: counter is filled in LLL */
|
|
|
|
lll_adv_iso->enc = 1U;
|
|
} else {
|
|
lll_adv_iso->enc = 0U;
|
|
}
|
|
|
|
err = ull_adv_sync_add_to_acad(lll_adv_sync, pdu_prev, pdu, bi_ad,
|
|
pdu_big_info_size + PDU_ADV_DATA_HEADER_SIZE);
|
|
if (err) {
|
|
/* Failed to add BIGInfo into the ACAD of the Periodic
|
|
* Advertising.
|
|
*/
|
|
|
|
/* Release allocated link buffers */
|
|
ll_rx_link_release(link_cmplt);
|
|
ll_rx_link_release(link_term);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Associate the ISO instance with an Extended Advertising instance */
|
|
lll_adv_iso->adv = &adv->lll;
|
|
|
|
/* Store the link buffer for ISO create and terminate complete event */
|
|
adv_iso->node_rx_complete.hdr.link = link_cmplt;
|
|
adv_iso->node_rx_terminate.rx.hdr.link = link_term;
|
|
|
|
/* Initialise LLL header members */
|
|
lll_hdr_init(lll_adv_iso, adv_iso);
|
|
|
|
/* Start sending BIS empty data packet for each BIS */
|
|
ret = adv_iso_start(adv_iso, iso_interval_us);
|
|
if (ret) {
|
|
/* Failed to schedule BIG events */
|
|
|
|
/* Reset the association of ISO instance with the Extended
|
|
* Advertising Instance
|
|
*/
|
|
lll_adv_iso->adv = NULL;
|
|
|
|
/* Release allocated link buffers */
|
|
ll_rx_link_release(link_cmplt);
|
|
ll_rx_link_release(link_term);
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
}
|
|
|
|
/* Associate the ISO instance with a Periodic Advertising */
|
|
lll_adv_sync->iso = lll_adv_iso;
|
|
|
|
#if defined(CONFIG_BT_TICKER_EXT_EXPIRE_INFO)
|
|
/* Notify the sync instance */
|
|
ull_adv_sync_iso_created(HDR_LLL2ULL(lll_adv_sync));
|
|
#endif /* CONFIG_BT_TICKER_EXT_EXPIRE_INFO */
|
|
|
|
/* Commit the BIGInfo in the ACAD field of Periodic Advertising */
|
|
lll_adv_sync_data_enqueue(lll_adv_sync, ter_idx);
|
|
|
|
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,
|
|
uint16_t max_pdu, uint8_t phy, uint8_t packing,
|
|
uint8_t framing, uint8_t bn, uint8_t irc,
|
|
uint8_t pto, uint8_t encryption, uint8_t *bcode)
|
|
{
|
|
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)
|
|
{
|
|
struct lll_adv_sync *lll_adv_sync;
|
|
struct lll_adv_iso *lll_adv_iso;
|
|
struct ll_adv_iso_set *adv_iso;
|
|
struct pdu_adv *pdu_prev, *pdu;
|
|
struct node_rx_pdu *node_rx;
|
|
struct lll_adv *lll_adv;
|
|
struct ll_adv_set *adv;
|
|
uint16_t stream_handle;
|
|
uint16_t handle;
|
|
uint8_t num_bis;
|
|
uint8_t ter_idx;
|
|
uint8_t err;
|
|
|
|
adv_iso = adv_iso_get(big_handle);
|
|
if (!adv_iso) {
|
|
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
|
|
}
|
|
|
|
lll_adv_iso = &adv_iso->lll;
|
|
lll_adv = lll_adv_iso->adv;
|
|
if (!lll_adv) {
|
|
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
|
|
}
|
|
|
|
if (lll_adv_iso->term_req) {
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
}
|
|
|
|
/* Remove ISO data path, keeping data from entering Tx pipeline */
|
|
num_bis = lll_adv_iso->num_bis;
|
|
while (num_bis--) {
|
|
stream_handle = lll_adv_iso->stream_handle[num_bis];
|
|
handle = LL_BIS_ADV_HANDLE_FROM_IDX(stream_handle);
|
|
(void)ll_remove_iso_path(handle,
|
|
BIT(BT_HCI_DATAPATH_DIR_HOST_TO_CTLR));
|
|
}
|
|
|
|
lll_adv_sync = lll_adv->sync;
|
|
adv = HDR_LLL2ULL(lll_adv);
|
|
|
|
/* Allocate next PDU */
|
|
err = ull_adv_sync_pdu_alloc(adv, ULL_ADV_PDU_EXTRA_DATA_ALLOC_IF_EXIST,
|
|
&pdu_prev, &pdu, NULL, NULL, &ter_idx);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Remove BigInfo from ACAD in AUX_SYNC_IND */
|
|
err = ull_adv_sync_remove_from_acad(lll_adv_sync, pdu_prev, pdu, BT_DATA_BIG_INFO);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
lll_adv_sync_data_enqueue(lll_adv_sync, ter_idx);
|
|
|
|
/* Prepare BIG terminate event, will be enqueued after tx flush */
|
|
node_rx = (void *)&adv_iso->node_rx_terminate;
|
|
node_rx->hdr.type = NODE_RX_TYPE_BIG_TERMINATE;
|
|
node_rx->hdr.handle = big_handle;
|
|
node_rx->rx_ftr.param = adv_iso;
|
|
|
|
if (reason == BT_HCI_ERR_REMOTE_USER_TERM_CONN) {
|
|
*((uint8_t *)node_rx->pdu) = BT_HCI_ERR_LOCALHOST_TERM_CONN;
|
|
} else {
|
|
*((uint8_t *)node_rx->pdu) = reason;
|
|
}
|
|
|
|
/* Request terminate procedure */
|
|
lll_adv_iso->term_reason = reason;
|
|
lll_adv_iso->term_req = 1U;
|
|
|
|
return BT_HCI_ERR_SUCCESS;
|
|
}
|
|
|
|
int ull_adv_iso_init(void)
|
|
{
|
|
int err;
|
|
|
|
err = init_reset();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ull_adv_iso_reset(void)
|
|
{
|
|
uint8_t handle;
|
|
int err;
|
|
|
|
handle = CONFIG_BT_CTLR_ADV_ISO_SET;
|
|
while (handle--) {
|
|
struct lll_adv_sync *adv_sync_lll;
|
|
struct lll_adv_iso *adv_iso_lll;
|
|
struct ll_adv_iso_set *adv_iso;
|
|
volatile uint32_t ret_cb;
|
|
struct lll_adv *adv_lll;
|
|
uint32_t ret;
|
|
void *mark;
|
|
|
|
adv_iso = &ll_adv_iso[handle];
|
|
adv_iso_lll = &adv_iso->lll;
|
|
adv_lll = adv_iso_lll->adv;
|
|
if (!adv_lll) {
|
|
continue;
|
|
}
|
|
|
|
mark = ull_disable_mark(adv_iso);
|
|
LL_ASSERT(mark == adv_iso);
|
|
|
|
/* Stop event scheduling */
|
|
ret_cb = TICKER_STATUS_BUSY;
|
|
ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_THREAD,
|
|
TICKER_ID_ADV_ISO_BASE + adv_iso_lll->handle,
|
|
ull_ticker_status_give, (void *)&ret_cb);
|
|
ret = ull_ticker_status_take(ret, &ret_cb);
|
|
if (ret) {
|
|
mark = ull_disable_unmark(adv_iso);
|
|
LL_ASSERT(mark == adv_iso);
|
|
|
|
/* Assert as there shall be a ticker instance active */
|
|
LL_ASSERT(false);
|
|
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
}
|
|
|
|
/* Abort any events in LLL pipeline */
|
|
err = ull_disable(adv_iso_lll);
|
|
LL_ASSERT(!err || (err == -EALREADY));
|
|
|
|
mark = ull_disable_unmark(adv_iso);
|
|
LL_ASSERT(mark == adv_iso);
|
|
|
|
/* Reset associated streams */
|
|
while (adv_iso_lll->num_bis--) {
|
|
struct lll_adv_iso_stream *stream;
|
|
uint16_t stream_handle;
|
|
|
|
stream_handle = adv_iso_lll->stream_handle[adv_iso_lll->num_bis];
|
|
stream = ull_adv_iso_stream_get(stream_handle);
|
|
if (stream) {
|
|
stream->link_tx_free = NULL;
|
|
}
|
|
}
|
|
|
|
/* Remove Periodic Advertising association */
|
|
adv_sync_lll = adv_lll->sync;
|
|
if (adv_sync_lll) {
|
|
adv_sync_lll->iso = NULL;
|
|
}
|
|
|
|
/* Remove Extended Advertising association */
|
|
adv_iso_lll->adv = NULL;
|
|
}
|
|
|
|
err = init_reset();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct ll_adv_iso_set *ull_adv_iso_get(uint8_t handle)
|
|
{
|
|
return adv_iso_get(handle);
|
|
}
|
|
|
|
uint8_t ull_adv_iso_chm_update(void)
|
|
{
|
|
uint8_t handle;
|
|
|
|
handle = CONFIG_BT_CTLR_ADV_ISO_SET;
|
|
while (handle--) {
|
|
(void)adv_iso_chm_update(handle);
|
|
}
|
|
|
|
/* TODO: Should failure due to Channel Map Update being already in
|
|
* progress be returned to caller?
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
void ull_adv_iso_chm_complete(struct node_rx_pdu *rx)
|
|
{
|
|
struct lll_adv_sync *sync_lll;
|
|
struct lll_adv_iso *iso_lll;
|
|
struct lll_adv *adv_lll;
|
|
|
|
iso_lll = rx->rx_ftr.param;
|
|
adv_lll = iso_lll->adv;
|
|
sync_lll = adv_lll->sync;
|
|
|
|
/* Update Channel Map in BIGInfo in the Periodic Advertising PDU */
|
|
while (sync_lll->iso_chm_done_req != sync_lll->iso_chm_done_ack) {
|
|
sync_lll->iso_chm_done_ack = sync_lll->iso_chm_done_req;
|
|
|
|
adv_iso_chm_complete_commit(iso_lll);
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_CTLR_HCI_ADV_HANDLE_MAPPING)
|
|
uint8_t ll_adv_iso_by_hci_handle_get(uint8_t hci_handle, uint8_t *handle)
|
|
{
|
|
struct ll_adv_iso_set *adv_iso;
|
|
uint8_t idx;
|
|
|
|
adv_iso = &ll_adv_iso[0];
|
|
|
|
for (idx = 0U; idx < CONFIG_BT_CTLR_ADV_ISO_SET; idx++, adv_iso++) {
|
|
if (adv_iso->lll.adv &&
|
|
(adv_iso->hci_handle == hci_handle)) {
|
|
*handle = idx;
|
|
return 0U;
|
|
}
|
|
}
|
|
|
|
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
|
|
}
|
|
|
|
uint8_t ll_adv_iso_by_hci_handle_new(uint8_t hci_handle, uint8_t *handle)
|
|
{
|
|
struct ll_adv_iso_set *adv_iso, *adv_iso_empty;
|
|
uint8_t idx;
|
|
|
|
adv_iso = &ll_adv_iso[0];
|
|
adv_iso_empty = NULL;
|
|
|
|
for (idx = 0U; idx < CONFIG_BT_CTLR_ADV_ISO_SET; idx++, adv_iso++) {
|
|
if (adv_iso->lll.adv) {
|
|
if (adv_iso->hci_handle == hci_handle) {
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
}
|
|
} else if (!adv_iso_empty) {
|
|
adv_iso_empty = adv_iso;
|
|
*handle = idx;
|
|
}
|
|
}
|
|
|
|
if (adv_iso_empty) {
|
|
memset(adv_iso_empty, 0U, sizeof(*adv_iso_empty));
|
|
adv_iso_empty->hci_handle = hci_handle;
|
|
return 0U;
|
|
}
|
|
|
|
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
|
|
}
|
|
#endif /* CONFIG_BT_CTLR_HCI_ADV_HANDLE_MAPPING */
|
|
|
|
void ull_adv_iso_offset_get(struct ll_adv_sync_set *sync)
|
|
{
|
|
static memq_link_t link;
|
|
static struct mayfly mfy = {0U, 0U, &link, NULL, mfy_iso_offset_get};
|
|
uint32_t ret;
|
|
|
|
mfy.param = sync;
|
|
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 1,
|
|
&mfy);
|
|
LL_ASSERT(!ret);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_TICKER_EXT_EXPIRE_INFO)
|
|
void ull_adv_iso_lll_biginfo_fill(struct pdu_adv *pdu, struct lll_adv_sync *lll_sync)
|
|
{
|
|
struct lll_adv_iso *lll_iso;
|
|
uint16_t latency_prepare;
|
|
struct pdu_big_info *bi;
|
|
uint64_t payload_count;
|
|
|
|
lll_iso = lll_sync->iso;
|
|
|
|
/* Calculate current payload count. If refcount is non-zero, we have called
|
|
* prepare and the LLL implementation has incremented latency_prepare already.
|
|
* In this case we need to subtract lazy + 1 from latency_prepare
|
|
*/
|
|
latency_prepare = lll_iso->latency_prepare;
|
|
if (ull_ref_get(HDR_LLL2ULL(lll_iso))) {
|
|
/* We are in post-prepare. latency_prepare is already
|
|
* incremented by lazy + 1 for next event
|
|
*/
|
|
latency_prepare -= lll_iso->iso_lazy + 1;
|
|
}
|
|
|
|
payload_count = lll_iso->payload_count + ((latency_prepare +
|
|
lll_iso->iso_lazy) * lll_iso->bn);
|
|
|
|
bi = big_info_get(pdu);
|
|
big_info_offset_fill(bi, lll_iso->ticks_sync_pdu_offset, 0U);
|
|
/* Assign the 39-bit payload count, retaining the 1 MS bit framing value */
|
|
bi->payload_count_framing[0] = payload_count;
|
|
bi->payload_count_framing[1] = payload_count >> 8;
|
|
bi->payload_count_framing[2] = payload_count >> 16;
|
|
bi->payload_count_framing[3] = payload_count >> 24;
|
|
bi->payload_count_framing[4] &= ~0x7F;
|
|
bi->payload_count_framing[4] |= (payload_count >> 32) & 0x7F;
|
|
|
|
/* Update Channel Map in the BIGInfo until Thread context gets a
|
|
* chance to update the PDU with new Channel Map.
|
|
*/
|
|
if (lll_sync->iso_chm_done_req != lll_sync->iso_chm_done_ack) {
|
|
pdu_big_info_chan_map_phy_set(bi->chm_phy,
|
|
lll_iso->data_chan_map,
|
|
lll_iso->phy);
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_TICKER_EXT_EXPIRE_INFO */
|
|
|
|
void ull_adv_iso_done_complete(struct node_rx_event_done *done)
|
|
{
|
|
struct ll_adv_iso_set *adv_iso;
|
|
struct lll_adv_iso *lll;
|
|
struct node_rx_pdu *rx;
|
|
memq_link_t *link;
|
|
|
|
/* switch to normal prepare */
|
|
mfy_lll_prepare.fp = lll_adv_iso_prepare;
|
|
|
|
/* Get reference to ULL context */
|
|
adv_iso = CONTAINER_OF(done->param, struct ll_adv_iso_set, ull);
|
|
lll = &adv_iso->lll;
|
|
|
|
/* Prepare BIG complete event */
|
|
rx = (void *)&adv_iso->node_rx_complete;
|
|
link = rx->hdr.link;
|
|
if (!link) {
|
|
/* NOTE: When BIS events have overlapping prepare placed in
|
|
* in the pipeline, more than one done complete event
|
|
* will be generated, lets ignore the additional done
|
|
* events.
|
|
*/
|
|
return;
|
|
}
|
|
rx->hdr.link = NULL;
|
|
|
|
rx->hdr.type = NODE_RX_TYPE_BIG_COMPLETE;
|
|
rx->hdr.handle = lll->handle;
|
|
rx->rx_ftr.param = adv_iso;
|
|
|
|
ll_rx_put_sched(link, rx);
|
|
}
|
|
|
|
void ull_adv_iso_done_terminate(struct node_rx_event_done *done)
|
|
{
|
|
struct ll_adv_iso_set *adv_iso;
|
|
struct lll_adv_iso *lll;
|
|
uint32_t ret;
|
|
|
|
/* Get reference to ULL context */
|
|
adv_iso = CONTAINER_OF(done->param, struct ll_adv_iso_set, ull);
|
|
lll = &adv_iso->lll;
|
|
|
|
/* Skip if terminated already (we come here if pipeline being flushed */
|
|
if (unlikely(lll->handle == LLL_ADV_HANDLE_INVALID)) {
|
|
return;
|
|
}
|
|
|
|
ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH,
|
|
(TICKER_ID_ADV_ISO_BASE + lll->handle),
|
|
ticker_stop_op_cb, adv_iso);
|
|
LL_ASSERT((ret == TICKER_STATUS_SUCCESS) ||
|
|
(ret == TICKER_STATUS_BUSY));
|
|
|
|
/* Invalidate the handle */
|
|
lll->handle = LLL_ADV_HANDLE_INVALID;
|
|
}
|
|
|
|
struct ll_adv_iso_set *ull_adv_iso_by_stream_get(uint16_t handle)
|
|
{
|
|
if (handle >= CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT) {
|
|
return NULL;
|
|
}
|
|
|
|
return adv_iso_get(stream_pool[handle].big_handle);
|
|
}
|
|
|
|
struct lll_adv_iso_stream *ull_adv_iso_stream_get(uint16_t handle)
|
|
{
|
|
if (handle >= CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT) {
|
|
return NULL;
|
|
}
|
|
|
|
return &stream_pool[handle];
|
|
}
|
|
|
|
struct lll_adv_iso_stream *ull_adv_iso_lll_stream_get(uint16_t handle)
|
|
{
|
|
return ull_adv_iso_stream_get(handle);
|
|
}
|
|
|
|
void ull_adv_iso_stream_release(struct ll_adv_iso_set *adv_iso)
|
|
{
|
|
struct lll_adv_iso *lll;
|
|
|
|
lll = &adv_iso->lll;
|
|
while (lll->num_bis--) {
|
|
struct lll_adv_iso_stream *stream;
|
|
struct ll_iso_datapath *dp;
|
|
uint16_t stream_handle;
|
|
memq_link_t *link;
|
|
|
|
stream_handle = lll->stream_handle[lll->num_bis];
|
|
stream = ull_adv_iso_stream_get(stream_handle);
|
|
|
|
LL_ASSERT(!stream->link_tx_free);
|
|
link = memq_deinit(&stream->memq_tx.head,
|
|
&stream->memq_tx.tail);
|
|
LL_ASSERT(link);
|
|
stream->link_tx_free = link;
|
|
|
|
dp = stream->dp;
|
|
if (dp) {
|
|
stream->dp = NULL;
|
|
isoal_source_destroy(dp->source_hdl);
|
|
ull_iso_datapath_release(dp);
|
|
}
|
|
|
|
mem_release(stream, &stream_free);
|
|
}
|
|
|
|
/* Remove Periodic Advertising association */
|
|
lll->adv->sync->iso = NULL;
|
|
|
|
/* Remove Extended Advertising association */
|
|
lll->adv = NULL;
|
|
}
|
|
|
|
uint32_t ull_adv_iso_max_time_get(const struct ll_adv_iso_set *adv_iso)
|
|
{
|
|
return adv_iso_time_get(adv_iso, true);
|
|
}
|
|
|
|
static int init_reset(void)
|
|
{
|
|
/* Add initializations common to power up initialization and HCI reset
|
|
* initializations.
|
|
*/
|
|
|
|
mem_init((void *)stream_pool, sizeof(struct lll_adv_iso_stream),
|
|
CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT, &stream_free);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct ll_adv_iso_set *adv_iso_get(uint8_t handle)
|
|
{
|
|
if (handle >= CONFIG_BT_CTLR_ADV_ISO_SET) {
|
|
return NULL;
|
|
}
|
|
|
|
return &ll_adv_iso[handle];
|
|
}
|
|
|
|
static struct stream *adv_iso_stream_acquire(void)
|
|
{
|
|
return mem_acquire(&stream_free);
|
|
}
|
|
|
|
static uint16_t adv_iso_stream_handle_get(struct lll_adv_iso_stream *stream)
|
|
{
|
|
return mem_index_get(stream, stream_pool, sizeof(*stream));
|
|
}
|
|
|
|
static uint8_t ptc_calc(const struct lll_adv_iso *lll, uint32_t event_spacing,
|
|
uint32_t event_spacing_max)
|
|
{
|
|
if (event_spacing < event_spacing_max) {
|
|
uint32_t ptc;
|
|
uint8_t nse;
|
|
|
|
/* Possible maximum Pre-transmission Subevents per BIS.
|
|
* sub_interval is at least T_MSS_150 + MPT (hence a value in 8 bits or more), i.e.
|
|
* the below division and the subsequent multiplication with lll->bn does not
|
|
* overflow.
|
|
*/
|
|
ptc = ((event_spacing_max - event_spacing) /
|
|
(lll->sub_interval * lll->bn * lll->num_bis)) *
|
|
lll->bn;
|
|
|
|
/* Required NSE */
|
|
nse = lll->bn * lll->irc; /* 3 bits * 4 bits, total 7 bits */
|
|
|
|
/* Requested NSE is greater than Required NSE, Pre-Transmission offset has been
|
|
* provided.
|
|
*
|
|
* NOTE: This is the case under HCI test command use to create BIG, i.e. test_config
|
|
* variable is true.
|
|
*/
|
|
if (lll->nse > nse) {
|
|
/* Restrict PTC to number of available subevents */
|
|
ptc = MIN(ptc, lll->nse - nse);
|
|
} else {
|
|
/* No PTO requested, Zephyr Controller implementation here will try using
|
|
* Pre-Transmisson offset of BT_CTLR_ADV_ISO_PTO_MIN, i.e. restrict to a
|
|
* maximum of BN Pre-Transmission subevents per BIS. This allows for a
|
|
* better time diversity ensuring skipped or missing reception at the ISO
|
|
* Sync Receiver so it can still have another chance at receiving the ISO
|
|
* PDUs within the permitted maximum transport latency.
|
|
*
|
|
* Usecases where BAP Broadcast Audio Assistant role device has a drifting
|
|
* ACL Peripheral role active in the BAP Broadcast Audio Sink device.
|
|
*/
|
|
ptc = MIN(ptc, (lll->bn * BT_CTLR_ADV_ISO_PTO_GROUP_COUNT));
|
|
}
|
|
|
|
/* FIXME: Do not remember why ptc is 4 bits, it should be 5 bits as ptc is a
|
|
* running buffer offset related to nse. Fix ptc and ptc_curr definitions,
|
|
* until then lets have an assert check here.
|
|
*/
|
|
LL_ASSERT(ptc <= BIT_MASK(4));
|
|
|
|
return ptc;
|
|
}
|
|
|
|
return 0U;
|
|
}
|
|
|
|
static uint32_t adv_iso_time_get(const struct ll_adv_iso_set *adv_iso, bool max)
|
|
{
|
|
const struct lll_adv_iso *lll_iso;
|
|
uint32_t ctrl_spacing;
|
|
uint32_t pdu_spacing;
|
|
uint32_t time_us;
|
|
|
|
lll_iso = &adv_iso->lll;
|
|
|
|
pdu_spacing = PDU_BIS_US(lll_iso->max_pdu, lll_iso->enc, lll_iso->phy,
|
|
lll_iso->phy_flags) +
|
|
EVENT_MSS_US;
|
|
ctrl_spacing = PDU_BIS_US(sizeof(struct pdu_big_ctrl), lll_iso->enc,
|
|
lll_iso->phy, lll_iso->phy_flags);
|
|
|
|
/* 1. Maximum PDU transmission time in 1M/2M/S8 PHY is 17040 us, or
|
|
* represented in 15-bits.
|
|
* 2. NSE in the range 1 to 31 is represented in 5-bits
|
|
* 3. num_bis in the range 1 to 31 is represented in 5-bits
|
|
*
|
|
* Hence, worst case event time can be represented in 25-bits plus
|
|
* one each bit for added ctrl_spacing and radio event overheads. I.e.
|
|
* 27-bits required and sufficiently covered by using 32-bit data type
|
|
* for time_us.
|
|
*/
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CTLR_ADV_ISO_RESERVE_MAX) || max) {
|
|
/* Maximum time reservation for both sequential and interleaved
|
|
* packing.
|
|
*/
|
|
time_us = (pdu_spacing * lll_iso->nse * lll_iso->num_bis) +
|
|
ctrl_spacing;
|
|
|
|
} else if (lll_iso->bis_spacing >=
|
|
(lll_iso->sub_interval * lll_iso->nse)) {
|
|
/* Time reservation omitting PTC subevents in sequetial
|
|
* packing.
|
|
*/
|
|
time_us = pdu_spacing * ((lll_iso->nse * lll_iso->num_bis) -
|
|
lll_iso->ptc);
|
|
|
|
} else {
|
|
/* Time reservation omitting PTC subevents in interleaved
|
|
* packing.
|
|
*/
|
|
time_us = pdu_spacing * ((lll_iso->nse - lll_iso->ptc) *
|
|
lll_iso->num_bis);
|
|
}
|
|
|
|
/* Add implementation defined radio event overheads */
|
|
time_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US;
|
|
|
|
return time_us;
|
|
}
|
|
|
|
static uint32_t adv_iso_start(struct ll_adv_iso_set *adv_iso,
|
|
uint32_t iso_interval_us)
|
|
{
|
|
uint32_t ticks_slot_overhead;
|
|
uint32_t ticks_slot_offset;
|
|
volatile uint32_t ret_cb;
|
|
uint32_t ticks_anchor;
|
|
uint32_t ticks_slot;
|
|
uint32_t slot_us;
|
|
uint32_t ret;
|
|
int err;
|
|
|
|
ull_hdr_init(&adv_iso->ull);
|
|
|
|
slot_us = adv_iso_time_get(adv_iso, false);
|
|
|
|
adv_iso->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(slot_us);
|
|
|
|
ticks_slot_offset = HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US);
|
|
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
|
|
ticks_slot_overhead = ticks_slot_offset;
|
|
} else {
|
|
ticks_slot_overhead = 0U;
|
|
}
|
|
ticks_slot = adv_iso->ull.ticks_slot + ticks_slot_overhead;
|
|
|
|
/* Find the slot after Periodic Advertisings events */
|
|
ticks_anchor = ticker_ticks_now_get() +
|
|
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US);
|
|
err = ull_sched_adv_aux_sync_free_anchor_get(ticks_slot, &ticks_anchor);
|
|
if (!err) {
|
|
ticks_anchor += HAL_TICKER_US_TO_TICKS(
|
|
MAX(EVENT_MAFS_US,
|
|
EVENT_OVERHEAD_START_US) -
|
|
EVENT_OVERHEAD_START_US +
|
|
(EVENT_TICKER_RES_MARGIN_US << 1));
|
|
}
|
|
|
|
/* setup to use ISO create prepare function for first radio event */
|
|
mfy_lll_prepare.fp = lll_adv_iso_create_prepare;
|
|
|
|
ret_cb = TICKER_STATUS_BUSY;
|
|
ret = ticker_start(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_THREAD,
|
|
(TICKER_ID_ADV_ISO_BASE + adv_iso->lll.handle),
|
|
ticks_anchor, 0U,
|
|
HAL_TICKER_US_TO_TICKS(iso_interval_us),
|
|
HAL_TICKER_REMAINDER(iso_interval_us),
|
|
TICKER_NULL_LAZY, ticks_slot, ticker_cb, adv_iso,
|
|
ull_ticker_status_give, (void *)&ret_cb);
|
|
ret = ull_ticker_status_take(ret, &ret_cb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint8_t adv_iso_chm_update(uint8_t big_handle)
|
|
{
|
|
struct ll_adv_iso_set *adv_iso;
|
|
struct lll_adv_iso *lll_iso;
|
|
|
|
adv_iso = adv_iso_get(big_handle);
|
|
if (!adv_iso) {
|
|
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
|
|
}
|
|
|
|
lll_iso = &adv_iso->lll;
|
|
if (lll_iso->term_req ||
|
|
(lll_iso->chm_req != lll_iso->chm_ack)) {
|
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
}
|
|
|
|
/* Request channel map update procedure */
|
|
lll_iso->chm_chan_count = ull_chan_map_get(lll_iso->chm_chan_map);
|
|
lll_iso->chm_req++;
|
|
|
|
return BT_HCI_ERR_SUCCESS;
|
|
}
|
|
|
|
static void adv_iso_chm_complete_commit(struct lll_adv_iso *lll_iso)
|
|
{
|
|
struct pdu_adv *pdu_prev, *pdu;
|
|
struct lll_adv_sync *lll_sync;
|
|
struct pdu_big_info *bi;
|
|
struct ll_adv_set *adv;
|
|
uint8_t acad_len;
|
|
uint8_t ter_idx;
|
|
uint8_t ad_len;
|
|
uint8_t *acad;
|
|
uint8_t *ad;
|
|
uint8_t len;
|
|
uint8_t err;
|
|
|
|
/* Allocate next PDU */
|
|
adv = HDR_LLL2ULL(lll_iso->adv);
|
|
err = ull_adv_sync_pdu_alloc(adv, ULL_ADV_PDU_EXTRA_DATA_ALLOC_IF_EXIST,
|
|
&pdu_prev, &pdu, NULL, NULL, &ter_idx);
|
|
LL_ASSERT(!err);
|
|
|
|
/* Copy content */
|
|
err = ull_adv_sync_duplicate(pdu_prev, pdu);
|
|
LL_ASSERT(!err);
|
|
|
|
/* Get the current ACAD */
|
|
acad = ull_adv_sync_get_acad(pdu, &acad_len);
|
|
|
|
lll_sync = adv->lll.sync;
|
|
|
|
/* Dev assert if ACAD empty */
|
|
LL_ASSERT(acad_len);
|
|
|
|
/* Find the BIGInfo */
|
|
len = acad_len;
|
|
ad = acad;
|
|
do {
|
|
ad_len = ad[PDU_ADV_DATA_HEADER_LEN_OFFSET];
|
|
if (ad_len &&
|
|
(ad[PDU_ADV_DATA_HEADER_TYPE_OFFSET] == BT_DATA_BIG_INFO)) {
|
|
break;
|
|
}
|
|
|
|
ad_len += 1U;
|
|
|
|
LL_ASSERT(ad_len <= len);
|
|
|
|
ad += ad_len;
|
|
len -= ad_len;
|
|
} while (len);
|
|
LL_ASSERT(len);
|
|
|
|
/* Get reference to BIGInfo */
|
|
bi = (void *)&ad[PDU_ADV_DATA_HEADER_DATA_OFFSET];
|
|
|
|
/* Copy the new/current Channel Map */
|
|
pdu_big_info_chan_map_phy_set(bi->chm_phy, lll_iso->data_chan_map,
|
|
lll_iso->phy);
|
|
|
|
/* Commit the new PDU Buffer */
|
|
lll_adv_sync_data_enqueue(lll_sync, ter_idx);
|
|
}
|
|
|
|
static void mfy_iso_offset_get(void *param)
|
|
{
|
|
struct lll_adv_sync *lll_sync;
|
|
struct ll_adv_sync_set *sync;
|
|
struct lll_adv_iso *lll_iso;
|
|
uint32_t ticks_to_expire;
|
|
struct pdu_big_info *bi;
|
|
uint32_t ticks_current;
|
|
uint64_t payload_count;
|
|
struct pdu_adv *pdu;
|
|
uint8_t ticker_id;
|
|
uint16_t lazy;
|
|
uint8_t retry;
|
|
uint8_t id;
|
|
|
|
sync = param;
|
|
lll_sync = &sync->lll;
|
|
lll_iso = lll_sync->iso;
|
|
ticker_id = TICKER_ID_ADV_ISO_BASE + lll_iso->handle;
|
|
|
|
id = TICKER_NULL;
|
|
ticks_to_expire = 0U;
|
|
ticks_current = 0U;
|
|
retry = 4U;
|
|
do {
|
|
uint32_t volatile ret_cb;
|
|
uint32_t ticks_previous;
|
|
uint32_t ret;
|
|
bool success;
|
|
|
|
ticks_previous = ticks_current;
|
|
|
|
ret_cb = TICKER_STATUS_BUSY;
|
|
ret = ticker_next_slot_get_ext(TICKER_INSTANCE_ID_CTLR,
|
|
TICKER_USER_ID_ULL_LOW,
|
|
&id, &ticks_current,
|
|
&ticks_to_expire, NULL, &lazy,
|
|
NULL, NULL,
|
|
ticker_op_cb, (void *)&ret_cb);
|
|
if (ret == TICKER_STATUS_BUSY) {
|
|
/* Busy wait until Ticker Job is enabled after any Radio
|
|
* event is done using the Radio hardware. Ticker Job
|
|
* ISR is disabled during Radio events in LOW_LAT
|
|
* feature to avoid Radio ISR latencies.
|
|
*/
|
|
while (ret_cb == TICKER_STATUS_BUSY) {
|
|
ticker_job_sched(TICKER_INSTANCE_ID_CTLR,
|
|
TICKER_USER_ID_ULL_LOW);
|
|
}
|
|
}
|
|
|
|
success = (ret_cb == TICKER_STATUS_SUCCESS);
|
|
LL_ASSERT(success);
|
|
|
|
LL_ASSERT((ticks_current == ticks_previous) || retry--);
|
|
|
|
LL_ASSERT(id != TICKER_NULL);
|
|
} while (id != ticker_id);
|
|
|
|
payload_count = lll_iso->payload_count +
|
|
(((uint64_t)lll_iso->latency_prepare + lazy) * lll_iso->bn);
|
|
|
|
pdu = lll_adv_sync_data_latest_peek(lll_sync);
|
|
bi = big_info_get(pdu);
|
|
big_info_offset_fill(bi, ticks_to_expire, 0U);
|
|
/* Assign the 39-bit payload count, retaining the 1 MS bit framing value */
|
|
bi->payload_count_framing[0] = payload_count;
|
|
bi->payload_count_framing[1] = payload_count >> 8;
|
|
bi->payload_count_framing[2] = payload_count >> 16;
|
|
bi->payload_count_framing[3] = payload_count >> 24;
|
|
bi->payload_count_framing[4] &= ~0x7F;
|
|
bi->payload_count_framing[4] |= (payload_count >> 32) & 0x7F;
|
|
|
|
/* Update Channel Map in the BIGInfo until Thread context gets a
|
|
* chance to update the PDU with new Channel Map.
|
|
*/
|
|
if (lll_sync->iso_chm_done_req != lll_sync->iso_chm_done_ack) {
|
|
pdu_big_info_chan_map_phy_set(bi->chm_phy,
|
|
lll_iso->data_chan_map,
|
|
lll_iso->phy);
|
|
}
|
|
}
|
|
|
|
static void pdu_big_info_chan_map_phy_set(uint8_t *chm_phy, uint8_t *chan_map,
|
|
uint8_t phy)
|
|
{
|
|
(void)memcpy(chm_phy, chan_map, PDU_CHANNEL_MAP_SIZE);
|
|
chm_phy[4] &= 0x1F;
|
|
chm_phy[4] |= ((find_lsb_set(phy) - 1U) << 5);
|
|
}
|
|
|
|
static inline struct pdu_big_info *big_info_get(struct pdu_adv *pdu)
|
|
{
|
|
struct pdu_adv_com_ext_adv *p;
|
|
struct pdu_adv_ext_hdr *h;
|
|
uint8_t *ptr;
|
|
|
|
p = (void *)&pdu->adv_ext_ind;
|
|
h = (void *)p->ext_hdr_adv_data;
|
|
ptr = h->data;
|
|
|
|
/* No AdvA and TargetA */
|
|
|
|
/* traverse through CTE Info, if present */
|
|
if (h->cte_info) {
|
|
ptr += sizeof(struct pdu_cte_info);
|
|
}
|
|
|
|
/* traverse through ADI, if present */
|
|
if (h->adi) {
|
|
ptr += sizeof(struct pdu_adv_adi);
|
|
}
|
|
|
|
/* traverse through aux ptr, if present */
|
|
if (h->aux_ptr) {
|
|
ptr += sizeof(struct pdu_adv_aux_ptr);
|
|
}
|
|
|
|
/* No SyncInfo */
|
|
|
|
/* traverse through Tx Power, if present */
|
|
if (h->tx_pwr) {
|
|
ptr++;
|
|
}
|
|
|
|
/* FIXME: Parse and find the Length encoded AD Format */
|
|
ptr += 2;
|
|
|
|
return (void *)ptr;
|
|
}
|
|
|
|
static inline void big_info_offset_fill(struct pdu_big_info *bi,
|
|
uint32_t ticks_offset,
|
|
uint32_t start_us)
|
|
{
|
|
uint32_t offs;
|
|
|
|
offs = HAL_TICKER_TICKS_TO_US(ticks_offset) - start_us;
|
|
offs = offs / OFFS_UNIT_30_US;
|
|
if (!!(offs >> OFFS_UNIT_BITS)) {
|
|
PDU_BIG_INFO_OFFS_SET(bi, offs / (OFFS_UNIT_300_US /
|
|
OFFS_UNIT_30_US));
|
|
PDU_BIG_INFO_OFFS_UNITS_SET(bi, 1U);
|
|
} else {
|
|
PDU_BIG_INFO_OFFS_SET(bi, offs);
|
|
PDU_BIG_INFO_OFFS_UNITS_SET(bi, 0U);
|
|
}
|
|
}
|
|
|
|
static void ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
|
|
uint32_t remainder, uint16_t lazy, uint8_t force,
|
|
void *param)
|
|
{
|
|
static struct lll_prepare_param p;
|
|
struct ll_adv_iso_set *adv_iso = param;
|
|
uint32_t remainder_us;
|
|
uint64_t event_count;
|
|
uint32_t ret;
|
|
uint8_t ref;
|
|
|
|
DEBUG_RADIO_PREPARE_A(1);
|
|
|
|
event_count = adv_iso->lll.payload_count / adv_iso->lll.bn;
|
|
for (int i = 0; i < adv_iso->lll.num_bis; i++) {
|
|
uint16_t stream_handle = adv_iso->lll.stream_handle[i];
|
|
|
|
ull_iso_lll_event_prepare(LL_BIS_ADV_HANDLE_FROM_IDX(stream_handle), event_count);
|
|
}
|
|
|
|
/* Increment prepare reference count */
|
|
ref = ull_ref_inc(&adv_iso->ull);
|
|
LL_ASSERT(ref);
|
|
|
|
/* Append timing parameters */
|
|
p.ticks_at_expire = ticks_at_expire;
|
|
p.remainder = remainder;
|
|
p.lazy = lazy;
|
|
p.force = force;
|
|
p.param = &adv_iso->lll;
|
|
mfy_lll_prepare.param = &p;
|
|
|
|
/* Kick LLL prepare */
|
|
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_LLL, 0,
|
|
&mfy_lll_prepare);
|
|
LL_ASSERT(!ret);
|
|
|
|
/* Calculate the BIG reference point of current BIG event */
|
|
remainder_us = remainder;
|
|
hal_ticker_remove_jitter(&ticks_at_expire, &remainder_us);
|
|
ticks_at_expire &= HAL_TICKER_CNTR_MASK;
|
|
adv_iso->big_ref_point = isoal_get_wrapped_time_us(HAL_TICKER_TICKS_TO_US(ticks_at_expire),
|
|
(remainder_us +
|
|
EVENT_OVERHEAD_START_US));
|
|
|
|
DEBUG_RADIO_PREPARE_A(1);
|
|
}
|
|
|
|
static void ticker_op_cb(uint32_t status, void *param)
|
|
{
|
|
*((uint32_t volatile *)param) = status;
|
|
}
|
|
|
|
static void ticker_stop_op_cb(uint32_t status, void *param)
|
|
{
|
|
static memq_link_t link;
|
|
static struct mayfly mfy = {0U, 0U, &link, NULL, adv_iso_disable};
|
|
uint32_t ret;
|
|
|
|
LL_ASSERT(status == TICKER_STATUS_SUCCESS);
|
|
|
|
/* Check if any pending LLL events that need to be aborted */
|
|
mfy.param = param;
|
|
ret = mayfly_enqueue(TICKER_USER_ID_ULL_LOW,
|
|
TICKER_USER_ID_ULL_HIGH, 0U, &mfy);
|
|
LL_ASSERT(!ret);
|
|
}
|
|
|
|
static void adv_iso_disable(void *param)
|
|
{
|
|
struct ll_adv_iso_set *adv_iso;
|
|
struct ull_hdr *hdr;
|
|
|
|
/* Check ref count to determine if any pending LLL events in pipeline */
|
|
adv_iso = param;
|
|
hdr = &adv_iso->ull;
|
|
if (ull_ref_get(hdr)) {
|
|
static memq_link_t link;
|
|
static struct mayfly mfy = {0U, 0U, &link, NULL, lll_disable};
|
|
uint32_t ret;
|
|
|
|
mfy.param = &adv_iso->lll;
|
|
|
|
/* Setup disabled callback to be called when ref count
|
|
* returns to zero.
|
|
*/
|
|
LL_ASSERT(!hdr->disabled_cb);
|
|
hdr->disabled_param = mfy.param;
|
|
hdr->disabled_cb = disabled_cb;
|
|
|
|
/* Trigger LLL disable */
|
|
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH,
|
|
TICKER_USER_ID_LLL, 0U, &mfy);
|
|
LL_ASSERT(!ret);
|
|
} else {
|
|
/* No pending LLL events */
|
|
disabled_cb(&adv_iso->lll);
|
|
}
|
|
}
|
|
|
|
static void disabled_cb(void *param)
|
|
{
|
|
static memq_link_t link;
|
|
static struct mayfly mfy = {0U, 0U, &link, NULL, tx_lll_flush};
|
|
uint32_t ret;
|
|
|
|
mfy.param = param;
|
|
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH,
|
|
TICKER_USER_ID_LLL, 0U, &mfy);
|
|
LL_ASSERT(!ret);
|
|
}
|
|
|
|
static void tx_lll_flush(void *param)
|
|
{
|
|
struct ll_adv_iso_set *adv_iso;
|
|
struct lll_adv_iso *lll;
|
|
struct node_rx_pdu *rx;
|
|
memq_link_t *link;
|
|
uint8_t num_bis;
|
|
|
|
/* Get reference to ULL context */
|
|
lll = param;
|
|
|
|
/* Flush TX */
|
|
num_bis = lll->num_bis;
|
|
while (num_bis--) {
|
|
struct lll_adv_iso_stream *stream;
|
|
struct node_tx_iso *tx;
|
|
uint16_t stream_handle;
|
|
memq_link_t *link2;
|
|
uint16_t handle;
|
|
|
|
stream_handle = lll->stream_handle[num_bis];
|
|
handle = LL_BIS_ADV_HANDLE_FROM_IDX(stream_handle);
|
|
stream = ull_adv_iso_stream_get(stream_handle);
|
|
|
|
link2 = memq_dequeue(stream->memq_tx.tail, &stream->memq_tx.head,
|
|
(void **)&tx);
|
|
while (link2) {
|
|
tx->next = link2;
|
|
ull_iso_lll_ack_enqueue(handle, tx);
|
|
|
|
link2 = memq_dequeue(stream->memq_tx.tail,
|
|
&stream->memq_tx.head,
|
|
(void **)&tx);
|
|
}
|
|
}
|
|
|
|
/* Get the terminate structure reserved in the ISO context.
|
|
* The terminate reason and connection handle should already be
|
|
* populated before this mayfly function was scheduled.
|
|
*/
|
|
adv_iso = HDR_LLL2ULL(lll);
|
|
rx = (void *)&adv_iso->node_rx_terminate;
|
|
link = rx->hdr.link;
|
|
LL_ASSERT(link);
|
|
rx->hdr.link = NULL;
|
|
|
|
/* Enqueue the terminate towards ULL context */
|
|
ull_rx_put_sched(link, rx);
|
|
}
|