blob: 8b83c36425d147428070fda1e147641e8b901b5f [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <soc.h>
#include <zephyr/sys/byteorder.h>
#include "hal/cpu.h"
#include "hal/ccm.h"
#include "hal/radio.h"
#include "hal/ticker.h"
#include "util/util.h"
#include "util/memq.h"
#include "pdu.h"
#include "lll.h"
#include "lll_vendor.h"
#include "lll_clock.h"
#include "lll_chan.h"
#include "lll_sync_iso.h"
#include "lll_internal.h"
#include "lll_tim_internal.h"
#include "ll_feat.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_lll_sync_iso
#include "common/log.h"
#include "hal/debug.h"
static int init_reset(void);
static void prepare(void *param);
static void create_prepare_bh(void *param);
static void prepare_bh(void *param);
static int create_prepare_cb(struct lll_prepare_param *p);
static int prepare_cb(struct lll_prepare_param *p);
static int prepare_cb_common(struct lll_prepare_param *p);
static void abort_cb(struct lll_prepare_param *prepare_param, void *param);
static void isr_rx_estab(void *param);
static void isr_rx(void *param);
static void isr_rx_iso_data_valid(const struct lll_sync_iso *const lll,
uint8_t bis_idx, struct node_rx_pdu *node_rx);
static void isr_rx_iso_data_invalid(const struct lll_sync_iso *const lll,
uint8_t bis_idx, uint8_t bn,
struct node_rx_pdu *node_rx);
static void isr_rx_ctrl_recv(struct lll_sync_iso *lll, struct pdu_bis *pdu);
/* FIXME: Optimize by moving to a common place, as similar variable is used for
* connections too.
*/
static uint8_t trx_cnt;
static uint8_t crc_ok_anchor;
int lll_sync_iso_init(void)
{
int err;
err = init_reset();
if (err) {
return err;
}
return 0;
}
int lll_sync_iso_reset(void)
{
int err;
err = init_reset();
if (err) {
return err;
}
return 0;
}
void lll_sync_iso_create_prepare(void *param)
{
prepare(param);
create_prepare_bh(param);
}
void lll_sync_iso_prepare(void *param)
{
prepare(param);
prepare_bh(param);
}
static int init_reset(void)
{
return 0;
}
static void prepare(void *param)
{
struct lll_prepare_param *p;
struct lll_sync_iso *lll;
uint16_t elapsed;
int err;
err = lll_hfclock_on();
LL_ASSERT(err >= 0);
p = param;
/* Instants elapsed */
elapsed = p->lazy + 1U;
lll = p->param;
/* Save the (latency + 1) for use in event */
lll->latency_prepare += elapsed;
/* Accumulate window widening */
lll->window_widening_prepare_us += lll->window_widening_periodic_us *
elapsed;
if (lll->window_widening_prepare_us > lll->window_widening_max_us) {
lll->window_widening_prepare_us = lll->window_widening_max_us;
}
}
static void create_prepare_bh(void *param)
{
int err;
/* Invoke common pipeline handling of prepare */
err = lll_prepare(lll_is_abort_cb, abort_cb, create_prepare_cb, 0U,
param);
LL_ASSERT(!err || err == -EINPROGRESS);
}
static void prepare_bh(void *param)
{
int err;
/* Invoke common pipeline handling of prepare */
err = lll_prepare(lll_is_abort_cb, abort_cb, prepare_cb, 0U, param);
LL_ASSERT(!err || err == -EINPROGRESS);
}
static int create_prepare_cb(struct lll_prepare_param *p)
{
int err;
err = prepare_cb_common(p);
if (err) {
DEBUG_RADIO_START_O(1);
return 0;
}
radio_isr_set(isr_rx_estab, p->param);
DEBUG_RADIO_START_O(1);
return 0;
}
static int prepare_cb(struct lll_prepare_param *p)
{
int err;
err = prepare_cb_common(p);
if (err) {
DEBUG_RADIO_START_O(1);
return 0;
}
radio_isr_set(isr_rx, p->param);
DEBUG_RADIO_START_O(1);
return 0;
}
static int prepare_cb_common(struct lll_prepare_param *p)
{
struct node_rx_pdu *node_rx;
struct lll_sync_iso *lll;
uint32_t ticks_at_event;
uint32_t ticks_at_start;
uint16_t event_counter;
uint8_t access_addr[4];
uint16_t data_chan_id;
uint8_t data_chan_use;
uint32_t remainder_us;
uint8_t crc_init[3];
struct ull_hdr *ull;
uint32_t remainder;
uint32_t hcto;
uint8_t phy;
DEBUG_RADIO_START_O(1);
lll = p->param;
/* Deduce the latency */
lll->latency_event = lll->latency_prepare - 1U;
/* Calculate the current event counter value */
event_counter = (lll->payload_count / lll->bn) + lll->latency_event;
/* Update BIS packet counter to next value */
lll->payload_count += (lll->latency_prepare * lll->bn);
/* Reset accumulated latencies */
lll->latency_prepare = 0U;
/* Current window widening */
lll->window_widening_event_us += lll->window_widening_prepare_us;
lll->window_widening_prepare_us = 0U;
if (lll->window_widening_event_us > lll->window_widening_max_us) {
lll->window_widening_event_us = lll->window_widening_max_us;
}
/* Initialize to mandatory parameter values */
lll->bis_curr = 1U;
lll->ptc_curr = 0U;
lll->irc_curr = 1U;
lll->bn_curr = 1U;
/* Initialize control subevent flag */
lll->ctrl = 0U;
/* Initialize stream current */
lll->stream_curr = 0U;
/* Initialize trx chain count */
trx_cnt = 0U;
/* Initialize anchor point CRC ok flag */
crc_ok_anchor = 0U;
/* Calculate the Access Address for the BIS event */
util_bis_aa_le32(lll->bis_curr, lll->seed_access_addr, access_addr);
data_chan_id = lll_chan_id(access_addr);
/* Calculate the CRC init value for the BIS event,
* preset with the BaseCRCInit value from the BIGInfo data the most
* significant 2 octets and the BIS_Number for the specific BIS in the
* least significant octet.
*/
crc_init[0] = lll->bis_curr;
(void)memcpy(&crc_init[1], lll->base_crc_init, sizeof(uint16_t));
/* Calculate the radio channel to use for ISO event */
data_chan_use = lll_chan_iso_event(event_counter, data_chan_id,
lll->data_chan_map,
lll->data_chan_count,
&lll->data_chan_prn_s,
&lll->data_chan_remap_idx);
lll->ctrl_chan_use = data_chan_use;
/* Start setting up of Radio h/w */
radio_reset();
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
radio_tx_power_set(lll->tx_pwr_lvl);
#else
radio_tx_power_set(RADIO_TXP_DEFAULT);
#endif
phy = lll->phy;
radio_phy_set(phy, PHY_FLAGS_S8);
radio_pkt_configure(RADIO_PKT_CONF_LENGTH_8BIT, lll->max_pdu,
RADIO_PKT_CONF_PHY(phy));
radio_aa_set(access_addr);
radio_crc_configure(PDU_CRC_POLYNOMIAL, sys_get_le24(crc_init));
lll_chan_set(data_chan_use);
node_rx = ull_iso_pdu_rx_alloc_peek(1U);
LL_ASSERT(node_rx);
radio_pkt_rx_set(node_rx->pdu);
radio_switch_complete_and_disable();
ticks_at_event = p->ticks_at_expire;
ull = HDR_LLL2ULL(lll);
ticks_at_event += lll_event_offset_get(ull);
ticks_at_start = ticks_at_event;
ticks_at_start += HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US);
remainder = p->remainder;
remainder_us = radio_tmr_start(0U, ticks_at_start, remainder);
radio_tmr_ready_save(remainder_us);
radio_tmr_aa_save(0U);
radio_tmr_aa_capture();
hcto = remainder_us +
((EVENT_JITTER_US + EVENT_TICKER_RES_MARGIN_US +
lll->window_widening_event_us) << 1) +
lll->window_size_event_us;
hcto += radio_rx_ready_delay_get(lll->phy, PHY_FLAGS_S8);
hcto += addr_us_get(lll->phy);
hcto += radio_rx_chain_delay_get(lll->phy, PHY_FLAGS_S8);
radio_tmr_hcto_configure(hcto);
radio_tmr_end_capture();
radio_rssi_measure();
#if defined(HAL_RADIO_GPIO_HAVE_LNA_PIN)
radio_gpio_lna_setup();
radio_gpio_pa_lna_enable(remainder_us +
radio_rx_ready_delay_get(lll->phy, PHY_FLAGS_S8) -
HAL_RADIO_GPIO_LNA_OFFSET);
#endif /* HAL_RADIO_GPIO_HAVE_LNA_PIN */
if (0) {
#if defined(CONFIG_BT_CTLR_XTAL_ADVANCED) && \
(EVENT_OVERHEAD_PREEMPT_US <= EVENT_OVERHEAD_PREEMPT_MIN_US)
/* check if preempt to start has changed */
} else if (lll_preempt_calc(ull, (TICKER_ID_SCAN_SYNC_ISO_BASE +
ull_sync_iso_lll_handle_get(lll)),
ticks_at_event)) {
radio_isr_set(lll_isr_abort, lll);
radio_disable();
return -ECANCELED;
#endif /* CONFIG_BT_CTLR_XTAL_ADVANCED */
} else {
uint32_t ret;
ret = lll_prepare_done(lll);
LL_ASSERT(!ret);
}
return 0;
}
static void abort_cb(struct lll_prepare_param *prepare_param, void *param)
{
struct event_done_extra *e;
int err;
/* NOTE: This is not a prepare being cancelled */
if (!prepare_param) {
radio_isr_set(lll_isr_done, param);
radio_disable();
return;
}
/* NOTE: Else clean the top half preparations of the aborted event
* currently in preparation pipeline.
*/
err = lll_hfclock_off();
LL_ASSERT(err >= 0);
/* Extra done event, to check sync lost */
e = ull_event_done_extra_get();
LL_ASSERT(e);
e->type = EVENT_DONE_EXTRA_TYPE_SYNC_ISO;
e->trx_cnt = 0U;
e->crc_valid = 0U;
lll_done(param);
}
static void isr_rx_estab(void *param)
{
struct event_done_extra *e;
uint8_t trx_done;
uint8_t crc_ok;
/* Read radio status and events */
trx_done = radio_is_done();
if (trx_done) {
crc_ok = radio_crc_is_valid();
trx_cnt++;
} else {
crc_ok = 0U;
}
/* Clear radio rx status and events */
lll_isr_rx_status_reset();
/* Calculate and place the drift information in done event */
e = ull_event_done_extra_get();
LL_ASSERT(e);
e->type = EVENT_DONE_EXTRA_TYPE_SYNC_ISO_ESTAB;
e->trx_cnt = trx_cnt;
e->crc_valid = crc_ok;
if (trx_cnt) {
struct lll_sync_iso *lll;
lll = param;
e->drift.preamble_to_addr_us = addr_us_get(lll->phy);
e->drift.start_to_address_actual_us =
radio_tmr_aa_get() - radio_tmr_ready_get();
e->drift.window_widening_event_us =
lll->window_widening_event_us;
/* Reset window widening, as anchor point sync-ed */
lll->window_widening_event_us = 0U;
lll->window_size_event_us = 0U;
}
lll_isr_cleanup(param);
}
static void isr_rx(void *param)
{
struct node_rx_pdu *node_rx;
struct event_done_extra *e;
struct lll_sync_iso *lll;
uint8_t access_addr[4];
uint16_t data_chan_id;
uint8_t data_chan_use;
uint8_t payload_index;
uint8_t crc_init[3];
uint8_t rssi_ready;
uint32_t start_us;
uint8_t new_burst;
uint8_t trx_done;
uint8_t bis_idx;
uint8_t skipped;
uint8_t crc_ok;
uint32_t hcto;
uint8_t bis;
uint8_t nse;
uint8_t bn;
/* Read radio status and events */
trx_done = radio_is_done();
if (!trx_done) {
/* Clear radio rx status and events */
lll_isr_rx_status_reset();
/* initialize LLL context reference */
lll = param;
/* BIS index */
bis_idx = lll->bis_curr - 1U;
goto isr_rx_done;
}
crc_ok = radio_crc_is_valid();
rssi_ready = radio_rssi_is_ready();
trx_cnt++;
/* Save the AA captured for the first anchor point sync */
if (!radio_tmr_aa_restore()) {
crc_ok_anchor = crc_ok;
radio_tmr_aa_save(radio_tmr_aa_get());
radio_tmr_ready_save(radio_tmr_ready_get());
}
/* Clear radio rx status and events */
lll_isr_rx_status_reset();
/* initialize LLL context reference */
lll = param;
/* BIS index */
bis_idx = lll->bis_curr - 1U;
/* Check CRC and generate ISO Data PDU */
if (crc_ok) {
struct lll_sync_iso_stream *stream;
struct pdu_bis *pdu;
uint16_t handle;
/* Check if Control Subevent being received */
if ((lll->bn_curr == lll->bn) &&
(lll->irc_curr == lll->irc) &&
(lll->ptc_curr == lll->ptc) &&
(lll->bis_curr == lll->num_bis) &&
lll->ctrl) {
lll->cssn_curr = lll->cssn_next;
node_rx = ull_iso_pdu_rx_alloc_peek(1U);
LL_ASSERT(node_rx);
pdu = (void *)node_rx->pdu;
if (pdu->ll_id != PDU_BIS_LLID_CTRL) {
goto isr_rx_done;
}
isr_rx_ctrl_recv(lll, pdu);
} else {
/* check if there are 2 free rx buffers, one will be
* consumed to receive the current PDU, and the other
* is to ensure a PDU can be setup for the radio DMA to
* receive in the next sub_interval/iso_interval.
*/
node_rx = ull_iso_pdu_rx_alloc_peek(2U);
if (!node_rx) {
goto isr_rx_done;
}
}
pdu = (void *)node_rx->pdu;
/* Check for new control PDU in control subevent */
if (pdu->cstf && (pdu->cssn != lll->cssn_curr)) {
lll->cssn_next = pdu->cssn;
/* TODO: check same CSSN is used in every subevent */
}
/* calculate the payload index in the sliding window */
payload_index = lll->payload_tail + (lll->bn_curr - 1U) +
(lll->ptc_curr * lll->pto);
if (payload_index >= lll->payload_count_max) {
payload_index -= lll->payload_count_max;
}
handle = lll->stream_handle[lll->stream_curr];
stream = ull_sync_iso_lll_stream_get(handle);
if ((lll->bis_curr == stream->bis_index) && pdu->len &&
!lll->payload[bis_idx][payload_index] &&
((payload_index >= lll->payload_tail) ||
(payload_index < lll->payload_head))) {
ull_iso_pdu_rx_alloc();
isr_rx_iso_data_valid(lll, bis_idx, node_rx);
lll->payload[bis_idx][payload_index] = node_rx;
}
}
isr_rx_done:
new_burst = 0U;
skipped = 0U;
isr_rx_find_subevent:
/* FIXME: Sequential or Interleaved BIS subevents decision */
/* Sequential Rx complete flow pseudo code */
while (lll->bn_curr < lll->bn) {
lll->bn_curr++;
payload_index = lll->payload_tail + (lll->bn_curr - 1U);
if (payload_index >= lll->payload_count_max) {
payload_index -= lll->payload_count_max;
}
if (!lll->payload[bis_idx][payload_index]) {
bis = lll->bis_curr;
/* Receive the (bn_curr)th Rx PDU of bis_curr */
goto isr_rx_next_subevent;
}
skipped++;
}
if (lll->irc_curr < lll->irc) {
if (!new_burst) {
lll->bn_curr = 1U;
lll->irc_curr++;
payload_index = lll->payload_tail;
if (payload_index >= lll->payload_count_max) {
payload_index -= lll->payload_count_max;
}
if (!lll->payload[bis_idx][payload_index]) {
bis = lll->bis_curr;
goto isr_rx_next_subevent;
} else {
skipped++;
new_burst = 1U;
/* Receive the missing (bn_curr)th Rx PDU of
* bis_curr
*/
goto isr_rx_find_subevent;
}
} else {
skipped += (lll->irc - lll->irc_curr) * lll->bn;
lll->irc_curr = lll->irc;
}
}
if (lll->ptc_curr < lll->ptc) {
lll->ptc_curr++;
/* Receive the (ptc_curr)th Rx PDU */
bis = lll->bis_curr;
goto isr_rx_next_subevent;
}
if (lll->bis_curr < lll->num_bis) {
struct lll_sync_iso_stream *stream;
uint16_t handle;
if (skipped) {
/* Calculate the Access Address for the current BIS */
util_bis_aa_le32(lll->bis_curr, lll->seed_access_addr,
access_addr);
data_chan_id = lll_chan_id(access_addr);
/* Skip channel indices for the previous BIS stream */
do {
/* Calculate the radio channel to use for ISO event */
(void)lll_chan_iso_subevent(data_chan_id,
lll->data_chan_map,
lll->data_chan_count,
&lll->data_chan_prn_s,
&lll->data_chan_remap_idx);
} while (--skipped);
}
handle = lll->stream_handle[lll->stream_curr];
stream = ull_sync_iso_lll_stream_get(handle);
if (lll->bis_curr == stream->bis_index) {
const uint8_t stream_curr = lll->stream_curr + 1U;
if (stream_curr < lll->stream_count) {
lll->stream_curr = stream_curr;
}
}
lll->bis_curr++;
lll->ptc_curr = 0U;
lll->irc_curr = 1U;
lll->bn_curr = 1U;
/* Receive the (bn_curr)th PDU of bis_curr */
bis = lll->bis_curr;
goto isr_rx_next_subevent;
}
if (!lll->ctrl && (lll->cssn_next != lll->cssn_curr)) {
/* Receive the control PDU and close the BIG event
* there after.
*/
lll->ctrl = 1U;
/* control subevent to use bis = 0 and se_n = 1 */
bis = 0U;
goto isr_rx_next_subevent;
}
/* Enqueue PDUs to ULL */
node_rx = NULL;
lll->stream_curr = 0U;
payload_index = lll->payload_tail;
for (bis_idx = 0U; bis_idx < lll->num_bis; bis_idx++) {
struct lll_sync_iso_stream *stream;
uint8_t payload_tail;
uint8_t stream_curr;
uint16_t handle;
handle = lll->stream_handle[lll->stream_curr];
stream = ull_sync_iso_lll_stream_get(handle);
/* Skip BIS indices not synchronized. bis_index is 0x01 to 0x1F,
* where as bis_idx is 0 indexed.
*/
if ((bis_idx + 1U) != stream->bis_index) {
continue;
}
payload_tail = lll->payload_tail;
bn = lll->bn;
while (bn--) {
if (lll->payload[bis_idx][payload_tail]) {
node_rx =
lll->payload[bis_idx][payload_tail];
lll->payload[bis_idx][payload_tail] = NULL;
iso_rx_put(node_rx->hdr.link, node_rx);
} else {
/* check if there are 2 free rx buffers, one
* will be consumed to generate PDU with invalid
* status, and the other is to ensure a PDU can
* be setup for the radio DMA to receive in the
* next sub_interval/iso_interval.
*/
node_rx = ull_iso_pdu_rx_alloc_peek(2U);
if (node_rx) {
struct pdu_bis *pdu;
ull_iso_pdu_rx_alloc();
pdu = (void *)node_rx->pdu;
pdu->ll_id = PDU_BIS_LLID_COMPLETE_END;
pdu->len = 0U;
isr_rx_iso_data_invalid(lll, bis_idx,
bn, node_rx);
iso_rx_put(node_rx->hdr.link, node_rx);
}
}
payload_index = payload_tail + 1U;
if (payload_index >= lll->payload_count_max) {
payload_index = 0U;
}
payload_tail = payload_index;
}
stream_curr = lll->stream_curr + 1U;
if (stream_curr < lll->stream_count) {
lll->stream_curr = stream_curr;
}
}
lll->payload_tail = payload_index;
#if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL)
if (node_rx) {
iso_rx_sched();
}
#endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */
e = ull_event_done_extra_get();
LL_ASSERT(e);
/* Check if BIG terminate procedure received */
if (lll->term_reason) {
e->type = EVENT_DONE_EXTRA_TYPE_SYNC_ISO_TERMINATE;
lll_isr_cleanup(param);
return;
/* Check if BIG Channel Map Update */
} else if (lll->chm_chan_count) {
const uint16_t event_counter = lll->payload_count / lll->bn;
/* Bluetooth Core Specification v5.3 Vol 6, Part B,
* Section 5.5.2 BIG Control Procedures
*
* When a Synchronized Receiver receives such a PDU where
* (instant - bigEventCounter) mod 65536 is greater than or
* equal to 32767 (because the instant is in the past), the
* the Link Layer may stop synchronization with the BIG.
*/
/* Note: We are not validating whether the control PDU was
* received after the instant but apply the new channel map.
* If the channel map was new at or after the instant and the
* the channel at the event counter did not match then the
* control PDU would not have been received.
*/
if (((event_counter - lll->ctrl_instant) & 0xFFFF) <= 0x7FFF) {
(void)memcpy(lll->data_chan_map, lll->chm_chan_map,
sizeof(lll->data_chan_map));
lll->data_chan_count = lll->chm_chan_count;
lll->chm_chan_count = 0U;
}
}
/* Calculate and place the drift information in done event */
e->type = EVENT_DONE_EXTRA_TYPE_SYNC_ISO;
e->trx_cnt = trx_cnt;
e->crc_valid = crc_ok_anchor;
if (trx_cnt) {
e->drift.preamble_to_addr_us = addr_us_get(lll->phy);
e->drift.start_to_address_actual_us =
radio_tmr_aa_restore() - radio_tmr_ready_restore();
e->drift.window_widening_event_us =
lll->window_widening_event_us;
/* Reset window widening, as anchor point sync-ed */
lll->window_widening_event_us = 0U;
lll->window_size_event_us = 0U;
}
lll_isr_cleanup(param);
return;
isr_rx_next_subevent:
/* Calculate the Access Address for the BIS event */
util_bis_aa_le32(bis, lll->seed_access_addr, access_addr);
data_chan_id = lll_chan_id(access_addr);
/* Calculate the CRC init value for the BIS event,
* preset with the BaseCRCInit value from the BIGInfo data the most
* significant 2 octets and the BIS_Number for the specific BIS in the
* least significant octet.
*/
crc_init[0] = bis;
(void)memcpy(&crc_init[1], lll->base_crc_init, sizeof(uint16_t));
radio_aa_set(access_addr);
radio_crc_configure(PDU_CRC_POLYNOMIAL, sys_get_le24(crc_init));
/* Set the channel to use */
if (bis) {
do {
/* Calculate the radio channel to use for ISO event */
data_chan_use = lll_chan_iso_subevent(data_chan_id,
lll->data_chan_map,
lll->data_chan_count,
&lll->data_chan_prn_s,
&lll->data_chan_remap_idx);
} while (skipped--);
} else {
data_chan_use = lll->ctrl_chan_use;
}
lll_chan_set(data_chan_use);
node_rx = ull_iso_pdu_rx_alloc_peek(1U);
LL_ASSERT(node_rx);
radio_pkt_rx_set(node_rx->pdu);
radio_switch_complete_and_disable();
/* PDU Header Complete TimeOut, calculate the absolute timeout in
* microseconds by when a PDU header is to be received for each
* subevent.
*/
nse = ((lll->bis_curr - 1U) * ((lll->bn * lll->irc) + lll->ptc)) +
((lll->irc_curr - 1U) * lll->bn) + (lll->bn_curr - 1U) +
lll->ptc_curr + lll->ctrl;
hcto = lll->sub_interval * nse;
if (trx_cnt) {
/* Setup radio packet timer header complete timeout for
* subsequent subevent PDU.
*/
/* Calculate the radio start with consideration of the drift
* based on the access address capture timestamp.
* Listen early considering +/- 2 us active clock jitter, i.e.
* listen early by 4 us.
*/
hcto += radio_tmr_aa_restore();
hcto -= radio_rx_chain_delay_get(lll->phy, PHY_FLAGS_S8);
hcto -= addr_us_get(lll->phy);
hcto -= radio_rx_ready_delay_get(lll->phy, PHY_FLAGS_S8);
hcto -= (EVENT_CLOCK_JITTER_US << 1);
start_us = hcto;
hcto = radio_tmr_start_us(0U, start_us);
/* Add 4 us + 4 us + (4 us * subevents so far), as radio
* was setup to listen 4 us early and subevents could have
* a 4 us drift each until the current subevent we are
* listening.
*/
hcto += ((EVENT_CLOCK_JITTER_US << 1) * (2U + nse)) +
RANGE_DELAY_US + HCTO_START_DELAY_US;
} else {
/* First subevent PDU was not received, hence setup radio packet
* timer header complete timeout from where the first subevent
* PDU which is the BIG event anchor point would have been
* received.
*/
hcto += radio_tmr_ready_restore();
start_us = hcto;
hcto = radio_tmr_start_us(0U, start_us);
hcto += ((EVENT_JITTER_US + EVENT_TICKER_RES_MARGIN_US +
lll->window_widening_event_us) << 1) +
lll->window_size_event_us;
}
/* header complete timeout to consider the radio ready delay, chain
* delay and access address duration.
*/
hcto += radio_rx_ready_delay_get(lll->phy, PHY_FLAGS_S8);
hcto += addr_us_get(lll->phy);
hcto += radio_rx_chain_delay_get(lll->phy, PHY_FLAGS_S8);
/* setup absolute PDU header reception timeout */
radio_tmr_hcto_configure(hcto);
/* setup capture of PDU end timestamp */
radio_tmr_end_capture();
#if defined(HAL_RADIO_GPIO_HAVE_LNA_PIN)
radio_gpio_lna_setup();
radio_gpio_pa_lna_enable(start_us +
radio_rx_ready_delay_get(lll->phy,
PHY_FLAGS_S8) -
HAL_RADIO_GPIO_LNA_OFFSET);
#endif /* HAL_RADIO_GPIO_HAVE_LNA_PIN */
}
static void isr_rx_iso_data_valid(const struct lll_sync_iso *const lll,
uint8_t bis_idx, struct node_rx_pdu *node_rx)
{
struct node_rx_iso_meta *iso_meta;
node_rx->hdr.type = NODE_RX_TYPE_ISO_PDU;
node_rx->hdr.handle = lll->stream_handle[bis_idx];
iso_meta = &node_rx->hdr.rx_iso_meta;
iso_meta->payload_number = lll->payload_count + (lll->bn_curr - 1U) +
(lll->ptc_curr * lll->pto) - lll->bn;
iso_meta->timestamp = HAL_TICKER_TICKS_TO_US(radio_tmr_start_get()) +
radio_tmr_aa_restore() - addr_us_get(lll->phy) +
(ceiling_fraction(lll->ptc_curr, lll->bn) *
lll->iso_interval * PERIODIC_INT_UNIT_US);
iso_meta->status = 0U;
}
static void isr_rx_iso_data_invalid(const struct lll_sync_iso *const lll,
uint8_t bis_idx, uint8_t bn,
struct node_rx_pdu *node_rx)
{
struct node_rx_iso_meta *iso_meta;
node_rx->hdr.type = NODE_RX_TYPE_ISO_PDU;
node_rx->hdr.handle = lll->stream_handle[bis_idx];
iso_meta = &node_rx->hdr.rx_iso_meta;
iso_meta->payload_number = lll->payload_count - bn - 1U;
iso_meta->timestamp = HAL_TICKER_TICKS_TO_US(radio_tmr_start_get()) +
radio_tmr_aa_restore() - addr_us_get(lll->phy);
iso_meta->status = 1U;
}
static void isr_rx_ctrl_recv(struct lll_sync_iso *lll, struct pdu_bis *pdu)
{
const uint8_t opcode = pdu->ctrl.opcode;
if (opcode == PDU_BIG_CTRL_TYPE_TERM_IND) {
if (!lll->term_reason) {
struct pdu_big_ctrl_term_ind *term;
term = (void *)&pdu->ctrl.term_ind;
lll->term_reason = term->reason;
lll->ctrl_instant = term->instant;
}
} else if (opcode == PDU_BIG_CTRL_TYPE_CHAN_MAP_IND) {
if (!lll->chm_chan_count) {
struct pdu_big_ctrl_chan_map_ind *chm;
uint8_t chan_count;
chm = (void *)&pdu->ctrl.chan_map_ind;
chan_count =
util_ones_count_get(chm->chm, sizeof(chm->chm));
if (chan_count >= CHM_USED_COUNT_MIN) {
lll->chm_chan_count = chan_count;
(void)memcpy(lll->chm_chan_map, chm->chm,
sizeof(lll->chm_chan_map));
lll->ctrl_instant = chm->instant;
}
}
} else {
/* Unknown control PDU, ignore */
}
}