blob: 4598ec50d21c519713a64a41255d17f3cd5b0746 [file] [log] [blame]
/*
* Copyright (c) 2017-2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <soc.h>
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include "hal/cpu.h"
#include "hal/ticker.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "util/mayfly.h"
#include "ticker/ticker.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_sync.h"
#include "lll/lll_df_types.h"
#include "lll_chan.h"
#include "ull_adv_types.h"
#include "ull_internal.h"
#include "ull_chan_internal.h"
#include "ull_adv_internal.h"
#include "ll.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_adv_sync
#include "common/log.h"
#include "hal/debug.h"
static int init_reset(void);
static inline struct ll_adv_sync_set *sync_acquire(void);
static inline void sync_release(struct ll_adv_sync_set *sync);
static inline uint16_t sync_handle_get(struct ll_adv_sync_set *sync);
static inline uint8_t sync_remove(struct ll_adv_sync_set *sync,
struct ll_adv_set *adv, uint8_t enable);
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
static inline void adv_sync_extra_data_set_clear(void *extra_data_prev,
void *extra_data_new,
uint16_t hdr_add_fields,
uint16_t hdr_rem_fields,
void *data);
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
static uint8_t adv_sync_hdr_set_clear(struct lll_adv_sync *lll_sync,
struct pdu_adv *ter_pdu_prev,
struct pdu_adv *ter_pdu,
uint16_t hdr_add_fields,
uint16_t hdr_rem_fields,
void *data);
static void mfy_sync_offset_get(void *param);
static inline struct pdu_adv_sync_info *sync_info_get(struct pdu_adv *pdu);
static inline void sync_info_offset_fill(struct pdu_adv_sync_info *si,
uint32_t ticks_offset,
uint32_t start_us);
static void ticker_cb(uint32_t ticks_at_expire, uint32_t remainder,
uint16_t lazy, uint8_t force, void *param);
static void ticker_op_cb(uint32_t status, void *param);
static struct ll_adv_sync_set ll_adv_sync_pool[CONFIG_BT_CTLR_ADV_SYNC_SET];
static void *adv_sync_free;
uint8_t ll_adv_sync_param_set(uint8_t handle, uint16_t interval, uint16_t flags)
{
struct lll_adv_sync *lll_sync;
struct ll_adv_sync_set *sync;
struct ll_adv_set *adv;
uint8_t err, ter_idx;
adv = ull_adv_is_created_get(handle);
if (!adv) {
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
}
lll_sync = adv->lll.sync;
if (!lll_sync) {
struct pdu_adv_com_ext_adv *ter_com_hdr;
struct pdu_adv_ext_hdr *ter_hdr;
struct pdu_adv *ter_pdu;
struct lll_adv *lll;
uint8_t *ter_dptr;
uint8_t ter_len;
int err;
sync = sync_acquire();
if (!sync) {
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
lll = &adv->lll;
lll_sync = &sync->lll;
lll->sync = lll_sync;
lll_sync->adv = lll;
lll_adv_data_reset(&lll_sync->data);
err = lll_adv_data_init(&lll_sync->data);
if (err) {
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
/* NOTE: ull_hdr_init(&sync->ull); is done on start */
lll_hdr_init(lll_sync, sync);
err = util_aa_le32(lll_sync->access_addr);
LL_ASSERT(!err);
lll_sync->data_chan_id = lll_chan_id(lll_sync->access_addr);
lll_sync->data_chan_count =
ull_chan_map_get(lll_sync->data_chan_map);
lll_csrand_get(lll_sync->crc_init, sizeof(lll_sync->crc_init));
lll_sync->latency_prepare = 0;
lll_sync->latency_event = 0;
lll_sync->event_counter = 0;
sync->is_enabled = 0U;
sync->is_started = 0U;
ter_pdu = lll_adv_sync_data_peek(lll_sync, NULL);
ter_pdu->type = PDU_ADV_TYPE_AUX_SYNC_IND;
ter_pdu->rfu = 0U;
ter_pdu->chan_sel = 0U;
ter_pdu->tx_addr = 0U;
ter_pdu->rx_addr = 0U;
ter_com_hdr = (void *)&ter_pdu->adv_ext_ind;
ter_hdr = (void *)ter_com_hdr->ext_hdr_adv_data;
ter_dptr = ter_hdr->data;
*(uint8_t *)ter_hdr = 0U;
/* Non-connectable and Non-scannable adv mode */
ter_com_hdr->adv_mode = 0U;
/* Calc tertiary PDU len */
ter_len = ull_adv_aux_hdr_len_calc(ter_com_hdr, &ter_dptr);
ull_adv_aux_hdr_len_fill(ter_com_hdr, ter_len);
ter_pdu->len = ter_len;
} else {
sync = HDR_LLL2ULL(lll_sync);
}
sync->interval = interval;
err = ull_adv_sync_pdu_set_clear(adv, 0, 0, NULL, &ter_idx);
if (err) {
return err;
}
lll_adv_sync_data_enqueue(lll_sync, ter_idx);
return 0;
}
uint8_t ll_adv_sync_ad_data_set(uint8_t handle, uint8_t op, uint8_t len,
uint8_t const *const data)
{
struct adv_pdu_field_data pdu_data;
struct lll_adv_sync *lll_sync;
struct ll_adv_set *adv;
uint8_t value[5];
uint8_t ter_idx;
uint8_t err;
/* TODO: handle other op values */
if (op != BT_HCI_LE_EXT_ADV_OP_COMPLETE_DATA &&
op != BT_HCI_LE_EXT_ADV_OP_UNCHANGED_DATA) {
/* FIXME: error code */
return BT_HCI_ERR_CMD_DISALLOWED;
}
adv = ull_adv_is_created_get(handle);
if (!adv) {
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
}
lll_sync = adv->lll.sync;
if (!lll_sync) {
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
}
pdu_data.field_data = value;
*pdu_data.field_data = len;
sys_put_le32((uint32_t)data, pdu_data.field_data + 1);
err = ull_adv_sync_pdu_set_clear(adv, ULL_ADV_PDU_HDR_FIELD_AD_DATA,
0, &pdu_data, &ter_idx);
if (err) {
return err;
}
lll_adv_sync_data_enqueue(lll_sync, ter_idx);
return 0;
}
uint8_t ll_adv_sync_enable(uint8_t handle, uint8_t enable)
{
struct lll_adv_sync *lll_sync;
struct ll_adv_sync_set *sync;
uint8_t sync_got_enabled;
struct ll_adv_set *adv;
uint8_t pri_idx;
uint8_t err;
adv = ull_adv_is_created_get(handle);
if (!adv) {
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
}
lll_sync = adv->lll.sync;
if (!lll_sync) {
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
}
sync = HDR_LLL2ULL(lll_sync);
if (!enable) {
if (!sync->is_enabled) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
if (!sync->is_started) {
sync->is_enabled = 0U;
return 0;
}
err = sync_remove(sync, adv, 0U);
return err;
}
/* TODO: Check for periodic data being complete */
/* TODO: Check packet too long */
sync_got_enabled = 0U;
if (sync->is_enabled) {
/* TODO: Enabling an already enabled advertising changes its
* random address.
*/
} else {
sync_got_enabled = 1U;
}
if (adv->is_enabled && !sync->is_started) {
uint32_t ticks_slot_overhead_aux;
struct lll_adv_aux *lll_aux;
struct ll_adv_aux_set *aux;
uint32_t ticks_anchor_sync;
uint32_t ticks_anchor_aux;
uint32_t ret;
lll_aux = adv->lll.aux;
/* Add sync_info into auxiliary PDU */
err = ull_adv_aux_hdr_set_clear(adv,
ULL_ADV_PDU_HDR_FIELD_SYNC_INFO,
0, NULL, NULL, &pri_idx);
if (err) {
return err;
}
if (lll_aux) {
/* FIXME: Find absolute ticks until after auxiliary PDU
* on air to place the periodic advertising PDU.
*/
ticks_anchor_aux = 0U; /* unused in this path */
ticks_slot_overhead_aux = 0U; /* unused in this path */
ticks_anchor_sync = ticker_ticks_now_get();
aux = NULL;
} else {
lll_aux = adv->lll.aux;
aux = HDR_LLL2ULL(lll_aux);
ticks_anchor_aux = ticker_ticks_now_get();
ticks_slot_overhead_aux = ull_adv_aux_evt_init(aux);
ticks_anchor_sync =
ticks_anchor_aux + ticks_slot_overhead_aux +
aux->ull.ticks_slot +
HAL_TICKER_US_TO_TICKS(EVENT_MAFS_US);
}
ret = ull_adv_sync_start(adv, sync, ticks_anchor_sync);
if (ret) {
sync_remove(sync, adv, 1U);
return BT_HCI_ERR_INSUFFICIENT_RESOURCES;
}
sync->is_started = 1U;
lll_adv_data_enqueue(&adv->lll, pri_idx);
if (aux) {
/* Keep aux interval equal or higher than primary PDU
* interval.
*/
aux->interval = adv->interval +
(HAL_TICKER_TICKS_TO_US(
ULL_ADV_RANDOM_DELAY) /
ADV_INT_UNIT_US);
ret = ull_adv_aux_start(aux, ticks_anchor_aux,
ticks_slot_overhead_aux);
if (ret) {
sync_remove(sync, adv, 1U);
return BT_HCI_ERR_INSUFFICIENT_RESOURCES;
}
aux->is_started = 1U;
}
}
if (sync_got_enabled) {
sync->is_enabled = sync_got_enabled;
}
return 0;
}
int ull_adv_sync_init(void)
{
int err;
err = init_reset();
if (err) {
return err;
}
return 0;
}
int ull_adv_sync_reset(void)
{
int err;
err = init_reset();
if (err) {
return err;
}
return 0;
}
uint16_t ull_adv_sync_lll_handle_get(struct lll_adv_sync *lll)
{
return sync_handle_get((void *)lll->hdr.parent);
}
uint32_t ull_adv_sync_start(struct ll_adv_set *adv,
struct ll_adv_sync_set *sync,
uint32_t ticks_anchor)
{
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
struct lll_df_adv_cfg *df_cfg;
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
uint32_t ticks_slot_overhead;
uint32_t volatile ret_cb;
uint32_t interval_us;
uint8_t sync_handle;
uint32_t slot_us;
uint32_t ret;
ull_hdr_init(&sync->ull);
/* TODO: Calc AUX_SYNC_IND slot_us */
slot_us = EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US;
slot_us += 1000;
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
df_cfg = adv->df_cfg;
if (df_cfg && df_cfg->is_enabled) {
slot_us += CTE_LEN_US(df_cfg->cte_length);
}
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
/* TODO: active_to_start feature port */
sync->ull.ticks_active_to_start = 0;
sync->ull.ticks_prepare_to_start =
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US);
sync->ull.ticks_preempt_to_start =
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US);
sync->ull.ticks_slot = HAL_TICKER_US_TO_TICKS(slot_us);
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
ticks_slot_overhead = MAX(sync->ull.ticks_active_to_start,
sync->ull.ticks_prepare_to_start);
} else {
ticks_slot_overhead = 0;
}
interval_us = (uint32_t)sync->interval * CONN_INT_UNIT_US;
sync_handle = sync_handle_get(sync);
ret_cb = TICKER_STATUS_BUSY;
ret = ticker_start(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_THREAD,
(TICKER_ID_ADV_SYNC_BASE + sync_handle),
ticks_anchor, 0,
HAL_TICKER_US_TO_TICKS(interval_us),
HAL_TICKER_REMAINDER(interval_us), TICKER_NULL_LAZY,
(sync->ull.ticks_slot + ticks_slot_overhead),
ticker_cb, sync,
ull_ticker_status_give, (void *)&ret_cb);
ret = ull_ticker_status_take(ret, &ret_cb);
return ret;
}
void ull_adv_sync_release(struct ll_adv_sync_set *sync)
{
lll_adv_sync_data_release(&sync->lll);
sync_release(sync);
}
void ull_adv_sync_offset_get(struct ll_adv_set *adv)
{
static memq_link_t link;
static struct mayfly mfy = {0, 0, &link, NULL, mfy_sync_offset_get};
uint32_t ret;
mfy.param = adv;
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 1,
&mfy);
LL_ASSERT(!ret);
}
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
void ull_adv_sync_update(struct ll_adv_sync_set *sync, uint32_t slot_plus_us,
uint32_t slot_minus_us)
{
uint32_t ret;
ret = ticker_update(TICKER_INSTANCE_ID_CTLR,
TICKER_USER_ID_THREAD,
(TICKER_ID_ADV_BASE + sync_handle_get(sync)),
0, 0,
slot_plus_us,
slot_minus_us,
0, 0,
NULL, NULL);
LL_ASSERT((ret == TICKER_STATUS_SUCCESS) ||
(ret == TICKER_STATUS_BUSY));
}
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
/* @brief Set or clear fields in extended advertising header and store
* extra_data if requested.
*
* @param[in] adv Advertising set.
* @param[in] hdr_add_fields Flag with information which fields add.
* @param[in] hdr_rem_fields Flag with information which fields remove.
* @param[in] data Pointer to data to be added to header and
* extra_data. Content depends on the value of
* @p hdr_add_fields.
* @param[out] ter_idx Index of new PDU.
*
* @Note
* @p data content depends on the flag provided by @p hdr_add_fields:
* - ULL_ADV_PDU_HDR_FIELD_CTE_INFO:
* # @p data->field_data points to single byte with CTEInfo field
* # @p data->extra_data points to memory where is struct lll_df_adv_cfg
* for LLL.
* - ULL_ADV_PDU_HDR_FIELD_AD_DATA:
* # @p data->field_data points to memory where first byte
* is size of advertising data, following byte is a pointer to actual
* advertising data.
* # @p data->extra_data is NULL
* - ULL_ADV_PDU_HDR_FIELD_AUX_PTR: # @p data parameter is not used
*
* @return Zero in case of success, other value in case of failure.
*/
uint8_t ull_adv_sync_pdu_set_clear(struct ll_adv_set *adv,
uint16_t hdr_add_fields,
uint16_t hdr_rem_fields,
struct adv_pdu_field_data *data,
uint8_t *ter_idx)
{
struct pdu_adv *pdu_prev, *pdu_new;
struct lll_adv_sync *lll_sync;
void *extra_data_prev;
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
void *extra_data;
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
int err;
lll_sync = adv->lll.sync;
if (!lll_sync) {
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
}
/* Get reference to previous periodic advertising PDU data */
pdu_prev = lll_adv_sync_data_peek(lll_sync, &extra_data_prev);
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
/* Get reference to new periodic advertising PDU data buffer */
if ((hdr_add_fields & ULL_ADV_PDU_HDR_FIELD_CTE_INFO) ||
(!(hdr_rem_fields & ULL_ADV_PDU_HDR_FIELD_CTE_INFO) &&
extra_data_prev)) {
/* If there was an extra data in past PDU data or it is required
* by the hdr_add_fields then allocate memmory for it.
*/
pdu_new = lll_adv_sync_data_alloc(lll_sync, &extra_data,
ter_idx);
if (!pdu_new) {
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
} else {
extra_data = NULL;
#else
{
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
pdu_new = lll_adv_sync_data_alloc(lll_sync, NULL, ter_idx);
if (!pdu_new) {
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
}
err = adv_sync_hdr_set_clear(lll_sync, pdu_prev, pdu_new,
hdr_add_fields, hdr_rem_fields,
(data ? data->field_data : NULL));
if (err) {
return err;
}
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
if (extra_data) {
adv_sync_extra_data_set_clear(extra_data_prev, extra_data,
hdr_add_fields, hdr_rem_fields,
(data ? data->extra_data : NULL));
}
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
return 0;
}
static int init_reset(void)
{
/* Initialize adv sync pool. */
mem_init(ll_adv_sync_pool, sizeof(struct ll_adv_sync_set),
sizeof(ll_adv_sync_pool) / sizeof(struct ll_adv_sync_set),
&adv_sync_free);
return 0;
}
static inline struct ll_adv_sync_set *sync_acquire(void)
{
return mem_acquire(&adv_sync_free);
}
static inline void sync_release(struct ll_adv_sync_set *sync)
{
mem_release(sync, &adv_sync_free);
}
static inline uint16_t sync_handle_get(struct ll_adv_sync_set *sync)
{
return mem_index_get(sync, ll_adv_sync_pool,
sizeof(struct ll_adv_sync_set));
}
static uint8_t sync_stop(struct ll_adv_sync_set *sync)
{
uint8_t sync_handle;
int err;
sync_handle = sync_handle_get(sync);
err = ull_ticker_stop_with_mark(TICKER_ID_ADV_SYNC_BASE + sync_handle,
sync, &sync->lll);
LL_ASSERT(err == 0 || err == -EALREADY);
if (err) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
return 0;
}
static inline uint8_t sync_remove(struct ll_adv_sync_set *sync,
struct ll_adv_set *adv, uint8_t enable)
{
uint8_t pri_idx;
uint8_t err;
/* Remove sync_info from auxiliary PDU */
err = ull_adv_aux_hdr_set_clear(adv, 0,
ULL_ADV_PDU_HDR_FIELD_SYNC_INFO,
NULL, NULL, &pri_idx);
if (err) {
return err;
}
lll_adv_data_enqueue(&adv->lll, pri_idx);
if (sync->is_started) {
/* TODO: we removed sync info, but if sync_stop() fails, what do
* we do?
*/
err = sync_stop(sync);
if (err) {
return err;
}
sync->is_started = 0U;
}
if (!enable) {
sync->is_enabled = 0U;
}
return 0U;
}
/* @brief Set or clear fields in extended advertising header.
*
* @param[in] adv Advertising set.
* @param[in] hdr_add_fields Flag with information which fields add.
* @param[in] hdr_rem_fields Flag with information which fields remove.
* @param[in] value Pointer to data to be added to header.
* Content depends on the value of
* @p hdr_add_fields.
* @param[out] ter_idx Index of new PDU.
*
* @Note
* @p value depends on the flag provided by @p hdr_add_fields.
* Information about content of value may be found in description of
* @ref ull_adv_sync_pdu_set_clear.
*
* @return Zero in case of success, other value in case of failure.
*/
static uint8_t adv_sync_hdr_set_clear(struct lll_adv_sync *lll_sync,
struct pdu_adv *ter_pdu_prev,
struct pdu_adv *ter_pdu,
uint16_t hdr_add_fields,
uint16_t hdr_rem_fields,
void *value)
{
struct pdu_adv_com_ext_adv *ter_com_hdr, *ter_com_hdr_prev;
struct pdu_adv_ext_hdr *ter_hdr, ter_hdr_prev;
uint8_t *ter_dptr, *ter_dptr_prev;
uint8_t acad_len_prev;
uint8_t ter_len_prev;
uint8_t hdr_buf_len;
uint16_t ter_len;
uint8_t *ad_data;
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
uint8_t cte_info;
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
uint8_t ad_len;
/* Get common pointers from reference to previous tertiary PDU data */
ter_com_hdr_prev = (void *)&ter_pdu_prev->adv_ext_ind;
ter_hdr = (void *)ter_com_hdr_prev->ext_hdr_adv_data;
ter_hdr_prev = *ter_hdr;
ter_dptr_prev = ter_hdr->data;
/* Set common fields in reference to new tertiary PDU data buffer */
ter_pdu->type = ter_pdu_prev->type;
ter_pdu->rfu = 0U;
ter_pdu->chan_sel = 0U;
ter_pdu->tx_addr = ter_pdu_prev->tx_addr;
ter_pdu->rx_addr = ter_pdu_prev->rx_addr;
ter_com_hdr = (void *)&ter_pdu->adv_ext_ind;
ter_com_hdr->adv_mode = ter_com_hdr_prev->adv_mode;
ter_hdr = (void *)ter_com_hdr->ext_hdr_adv_data;
ter_dptr = ter_hdr->data;
*(uint8_t *)ter_hdr = 0U;
/* No AdvA in AUX_SYNC_IND */
/* No TargetA in AUX_SYNC_IND */
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
/* If requested add or update CTEInfo */
if (hdr_add_fields & ULL_ADV_PDU_HDR_FIELD_CTE_INFO) {
ter_hdr->cte_info = 1;
cte_info = *(uint8_t *)value;
value = (uint8_t *)value + 1;
ter_dptr += sizeof(struct pdu_cte_info);
/* If CTEInfo exists in prev and is not requested to be removed */
} else if (!(hdr_rem_fields & ULL_ADV_PDU_HDR_FIELD_CTE_INFO) &&
ter_hdr_prev.cte_info) {
ter_hdr->cte_info = 1;
ter_dptr += sizeof(struct pdu_cte_info);
}
/* If CTEInfo exists in prev PDU */
if (ter_hdr_prev.cte_info) {
ter_dptr_prev += sizeof(struct pdu_cte_info);
}
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
/* No ADI in AUX_SYNC_IND */
/* AuxPtr - will be added if AUX_CHAIN_IND is required */
if ((hdr_add_fields & ULL_ADV_PDU_HDR_FIELD_AUX_PTR) ||
(!(hdr_rem_fields & ULL_ADV_PDU_HDR_FIELD_AUX_PTR) &&
ter_hdr_prev.aux_ptr)) {
ter_hdr->aux_ptr = 1;
}
if (ter_hdr->aux_ptr) {
ter_dptr += sizeof(struct pdu_adv_aux_ptr);
}
if (ter_hdr_prev.aux_ptr) {
ter_dptr_prev += sizeof(struct pdu_adv_aux_ptr);
}
/* No SyncInfo in AUX_SYNC_IND */
/* Tx Power flag */
if (ter_hdr_prev.tx_pwr) {
ter_dptr_prev++;
ter_hdr->tx_pwr = 1;
ter_dptr++;
}
/* Calc previous ACAD len and update PDU len */
ter_len_prev = ter_dptr_prev - (uint8_t *)ter_com_hdr_prev;
hdr_buf_len = ter_com_hdr_prev->ext_hdr_len +
PDU_AC_EXT_HEADER_SIZE_MIN;
if (ter_len_prev <= hdr_buf_len) {
acad_len_prev = hdr_buf_len - ter_len_prev;
ter_len_prev += acad_len_prev;
ter_dptr_prev += acad_len_prev;
ter_dptr += acad_len_prev;
} else {
acad_len_prev = 0;
/* NOTE: If no flags are set then extended header length will be
* zero. Under this condition the current ter_len_prev
* value will be greater than extended header length,
* hence set ter_len_prev to size of the length/mode
* field.
*/
ter_len_prev = PDU_AC_EXT_HEADER_SIZE_MIN;
ter_dptr_prev = (uint8_t *)ter_com_hdr_prev + ter_len_prev;
}
/* Did we parse beyond PDU length? */
if (ter_len_prev > ter_pdu_prev->len) {
/* we should not encounter invalid length */
return BT_HCI_ERR_UNSPECIFIED;
}
/* Calc current tertiary PDU len */
ter_len = ull_adv_aux_hdr_len_calc(ter_com_hdr, &ter_dptr);
ull_adv_aux_hdr_len_fill(ter_com_hdr, ter_len);
/* Get Adv data from function parameters */
if (hdr_add_fields & ULL_ADV_PDU_HDR_FIELD_AD_DATA) {
ad_data = value;
ad_len = *ad_data;
++ad_data;
ad_data = (void *)sys_get_le32(ad_data);
} else if (!(hdr_rem_fields & ULL_ADV_PDU_HDR_FIELD_AD_DATA)) {
ad_len = ter_pdu_prev->len - ter_len_prev;
ad_data = ter_dptr_prev;
} else {
ad_len = 0;
ad_data = NULL;
}
/* Add AD len to tertiary PDU length */
ter_len += ad_len;
/* Check AdvData overflow */
if (ter_len > PDU_AC_PAYLOAD_SIZE_MAX) {
return BT_HCI_ERR_PACKET_TOO_LONG;
}
/* set the secondary PDU len */
ter_pdu->len = ter_len;
/* Start filling tertiary PDU payload based on flags from here
* ==============================================================
*/
/* Fill AdvData in tertiary PDU */
memmove(ter_dptr, ad_data, ad_len);
/* Fill ACAD in tertiary PDU */
ter_dptr_prev -= acad_len_prev;
ter_dptr -= acad_len_prev;
memmove(ter_dptr, ter_dptr_prev, acad_len_prev);
/* Tx Power */
if (ter_hdr->tx_pwr) {
*--ter_dptr = *--ter_dptr_prev;
}
/* No SyncInfo in AUX_SYNC_IND */
/* AuxPtr */
if (ter_hdr->aux_ptr) {
/* ToDo Update setup of aux_ptr - check documentation */
if (ter_hdr_prev.aux_ptr) {
ter_dptr_prev -= sizeof(struct pdu_adv_aux_ptr);
ter_dptr -= sizeof(struct pdu_adv_aux_ptr);
memmove(ter_dptr, ter_dptr_prev,
sizeof(struct pdu_adv_aux_ptr));
} else {
ull_adv_aux_ptr_fill(&ter_dptr, lll_sync->adv->phy_s);
}
}
/* No ADI in AUX_SYNC_IND*/
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
if (ter_hdr->cte_info) {
if (hdr_add_fields & ULL_ADV_PDU_HDR_FIELD_CTE_INFO) {
*--ter_dptr = cte_info;
} else {
*--ter_dptr = *--ter_dptr_prev;
}
}
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
/* No TargetA in AUX_SYNC_IND */
/* No AdvA in AUX_SYNC_IND */
return 0;
}
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
/* @brief Set or clear fields in extended advertising header and store
* extra_data if requested.
*
* @param[in] extra_data_prev Pointer to previous content of extra_data.
* @param[in] hdr_add_fields Flag with information which fields add.
* @param[in] hdr_rem_fields Flag with information which fields remove.
* @param[in] data Pointer to data to be stored in extra_data.
* Content depends on the data depends on
* @p hdr_add_fields.
*
* @Note
* @p data depends on the flag provided by @p hdr_add_fields.
* Information about content of value may be found in description of
* @ref ull_adv_sync_pdu_set_clear.
*
* @return Zero in case of success, other value in case of failure.
*/
static inline void adv_sync_extra_data_set_clear(void *extra_data_prev,
void *extra_data_new,
uint16_t hdr_add_fields,
uint16_t hdr_rem_fields,
void *data)
{
/* Currently only CTE enable requires extra_data. Due to that fact
* CTE additional data are just copied to extra_data memory.
*/
if (hdr_add_fields & ULL_ADV_PDU_HDR_FIELD_CTE_INFO) {
memcpy(extra_data_new, data, sizeof(struct lll_df_adv_cfg));
} else if ((hdr_rem_fields & ULL_ADV_PDU_HDR_FIELD_CTE_INFO) ||
extra_data_prev) {
memmove(extra_data_new, extra_data_prev,
sizeof(struct lll_df_adv_cfg));
}
}
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
static void mfy_sync_offset_get(void *param)
{
struct ll_adv_set *adv = param;
struct lll_adv_sync *lll_sync;
struct ll_adv_sync_set *sync;
struct pdu_adv_sync_info *si;
uint32_t ticks_to_expire;
uint32_t ticks_current;
struct pdu_adv *pdu;
uint8_t ticker_id;
uint16_t lazy;
uint8_t retry;
uint8_t id;
lll_sync = adv->lll.sync;
sync = HDR_LLL2ULL(lll_sync);
ticker_id = TICKER_ID_ADV_SYNC_BASE + sync_handle_get(sync);
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;
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, &lazy,
ticker_op_cb, (void *)&ret_cb);
if (ret == TICKER_STATUS_BUSY) {
while (ret_cb == TICKER_STATUS_BUSY) {
ticker_job_sched(TICKER_INSTANCE_ID_CTLR,
TICKER_USER_ID_ULL_LOW);
}
}
LL_ASSERT(ret_cb == TICKER_STATUS_SUCCESS);
LL_ASSERT((ticks_current == ticks_previous) || retry--);
LL_ASSERT(id != TICKER_NULL);
} while (id != ticker_id);
/* NOTE: as remainder not used in scheduling primary PDU
* packet timer starts transmission after 1 tick hence the +1.
*/
lll_sync->ticks_offset = ticks_to_expire + 1;
pdu = lll_adv_aux_data_curr_get(adv->lll.aux);
si = sync_info_get(pdu);
sync_info_offset_fill(si, ticks_to_expire, 0);
si->evt_cntr = lll_sync->event_counter + lll_sync->latency_prepare +
lazy;
}
static inline struct pdu_adv_sync_info *sync_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;
if (h->adv_addr) {
ptr += BDADDR_SIZE;
}
if (h->adi) {
ptr += sizeof(struct pdu_adv_adi);
}
if (h->aux_ptr) {
ptr += sizeof(struct pdu_adv_aux_ptr);
}
return (void *)ptr;
}
static inline void sync_info_offset_fill(struct pdu_adv_sync_info *si,
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 >> 13)) {
si->offs = offs / (OFFS_UNIT_300_US / OFFS_UNIT_30_US);
si->offs_units = 1U;
} else {
si->offs = offs;
si->offs_units = 0U;
}
}
static void ticker_cb(uint32_t ticks_at_expire, uint32_t remainder,
uint16_t lazy, uint8_t force, void *param)
{
static memq_link_t link;
static struct mayfly mfy = {0, 0, &link, NULL, lll_adv_sync_prepare};
static struct lll_prepare_param p;
struct ll_adv_sync_set *sync = param;
struct lll_adv_sync *lll;
uint32_t ret;
uint8_t ref;
DEBUG_RADIO_PREPARE_A(1);
lll = &sync->lll;
/* Increment prepare reference count */
ref = ull_ref_inc(&sync->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 = lll;
mfy.param = &p;
/* Kick LLL prepare */
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH,
TICKER_USER_ID_LLL, 0, &mfy);
LL_ASSERT(!ret);
DEBUG_RADIO_PREPARE_A(1);
}
static void ticker_op_cb(uint32_t status, void *param)
{
*((uint32_t volatile *)param) = status;
}