zephyr/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c
Vinayak Kariappa Chettimada 19fe102862 Bluetooth: Controller: Propagate ticks_drift in the ticker callback
The ticker `ticks_drift` is propagated via the ticker
elapsed callback, in order to provide necessary information
to correctly calculate total elapsed durations by states and
roles that use ticker extensions to mitigate scheduling
collisions by drifting within a permitted window.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
2021-09-17 14:30:07 +02:00

355 lines
8.0 KiB
C

/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <bluetooth/bluetooth.h>
#include <sys/byteorder.h>
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "util/mayfly.h"
#include "hal/ccm.h"
#include "hal/radio.h"
#include "hal/ticker.h"
#include "ticker/ticker.h"
#include "pdu.h"
#include "lll.h"
#include "lll/lll_vendor.h"
#include "lll_clock.h"
#include "lll_scan.h"
#include "lll_sync.h"
#include "lll_sync_iso.h"
#include "ull_scan_types.h"
#include "ull_sync_types.h"
#include "ull_internal.h"
#include "ull_scan_internal.h"
#include "ull_sync_internal.h"
#include "ull_sync_iso_internal.h"
#include "ll.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_sync_iso
#include "common/log.h"
#include "hal/debug.h"
static int init_reset(void);
static inline struct ll_sync_iso *sync_iso_acquire(void);
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 struct ll_sync_iso ll_sync_iso_pool[CONFIG_BT_CTLR_SCAN_SYNC_ISO_SET];
static void *sync_iso_free;
uint8_t ll_big_sync_create(uint8_t big_handle, uint16_t sync_handle,
uint8_t encryption, uint8_t *bcode, uint8_t mse,
uint16_t sync_timeout, uint8_t num_bis,
uint8_t *bis)
{
struct ll_sync_iso *sync_iso;
memq_link_t *link_sync_estab;
memq_link_t *link_sync_lost;
struct node_rx_hdr *node_rx;
struct ll_sync_set *sync;
sync = ull_sync_is_enabled_get(sync_handle);
if (!sync || sync->iso.sync_iso) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
link_sync_estab = ll_rx_link_alloc();
if (!link_sync_estab) {
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
link_sync_lost = ll_rx_link_alloc();
if (!link_sync_lost) {
ll_rx_link_release(link_sync_estab);
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
node_rx = ll_rx_alloc();
if (!node_rx) {
ll_rx_link_release(link_sync_lost);
ll_rx_link_release(link_sync_estab);
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
sync_iso = sync_iso_acquire();
if (!sync_iso) {
ll_rx_release(node_rx);
ll_rx_link_release(link_sync_lost);
ll_rx_link_release(link_sync_estab);
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
/* Initialize the ISO sync ULL context */
sync_iso->sync = sync;
sync_iso->node_rx_lost.hdr.link = link_sync_lost;
/* Setup the periodic sync to establish ISO sync */
node_rx->link = link_sync_estab;
sync->iso.node_rx_estab = node_rx;
/* Initialize ULL and LLL headers */
ull_hdr_init(&sync_iso->ull);
lll_hdr_init(&sync_iso->lll, sync);
/* Enable periodic advertising to establish ISO sync */
sync->iso.sync_iso = sync_iso;
return BT_HCI_ERR_SUCCESS;
}
uint8_t ll_big_sync_terminate(uint8_t big_handle, void **rx)
{
struct ll_sync_iso *sync_iso;
struct node_rx_pdu *node_rx;
memq_link_t *link_sync_estab;
memq_link_t *link_sync_lost;
struct ll_sync_set *sync;
int err;
sync_iso = ull_sync_iso_get(big_handle);
if (!sync_iso) {
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
}
sync = sync_iso->sync;
if (sync) {
struct node_rx_sync_iso *se;
if (sync->iso.sync_iso != sync_iso) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
sync->iso.sync_iso = NULL;
sync_iso->sync = NULL;
node_rx = (void *)sync->iso.node_rx_estab;
link_sync_estab = node_rx->hdr.link;
link_sync_lost = sync_iso->node_rx_lost.hdr.link;
ll_rx_link_release(link_sync_lost);
ll_rx_link_release(link_sync_estab);
ll_rx_release(node_rx);
node_rx = (void *)&sync_iso->node_rx_lost;
node_rx->hdr.type = NODE_RX_TYPE_SYNC_ISO;
node_rx->hdr.handle = 0xffff;
/* NOTE: struct node_rx_lost has uint8_t member following the
* struct node_rx_hdr to store the reason.
*/
se = (void *)node_rx->pdu;
se->status = BT_HCI_ERR_OP_CANCELLED_BY_HOST;
/* NOTE: Since NODE_RX_TYPE_SYNC_ISO is only generated from ULL
* context, pass ULL context as parameter.
*/
node_rx->hdr.rx_ftr.param = sync_iso;
*rx = node_rx;
return BT_HCI_ERR_SUCCESS;
}
err = ull_ticker_stop_with_mark((TICKER_ID_SCAN_SYNC_ISO_BASE +
big_handle), sync_iso, &sync_iso->lll);
LL_ASSERT(err == 0 || err == -EALREADY);
if (err) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
link_sync_lost = sync_iso->node_rx_lost.hdr.link;
ll_rx_link_release(link_sync_lost);
ull_sync_iso_release(sync_iso);
return BT_HCI_ERR_SUCCESS;
}
int ull_sync_iso_init(void)
{
int err;
err = init_reset();
if (err) {
return err;
}
return 0;
}
int ull_sync_iso_reset(void)
{
int err;
err = init_reset();
if (err) {
return err;
}
return 0;
}
struct ll_sync_iso *ull_sync_iso_get(uint8_t handle)
{
if (handle >= CONFIG_BT_CTLR_SCAN_SYNC_ISO_SET) {
return NULL;
}
return &ll_sync_iso_pool[handle];
}
uint8_t ull_sync_iso_handle_get(struct ll_sync_iso *sync)
{
return mem_index_get(sync, ll_sync_iso_pool,
sizeof(struct ll_sync_iso));
}
uint8_t ull_sync_iso_lll_handle_get(struct lll_sync_iso *lll)
{
return ull_sync_handle_get(HDR_LLL2ULL(lll));
}
void ull_sync_iso_release(struct ll_sync_iso *sync_iso)
{
mem_release(sync_iso, &sync_iso_free);
}
void ull_sync_iso_setup(struct ll_sync_iso *sync_iso,
struct node_rx_hdr *node_rx,
struct pdu_big_info *big_info)
{
uint32_t ticks_slot_overhead;
struct node_rx_sync_iso *se;
uint32_t ticks_slot_offset;
struct lll_sync_iso *lll;
struct node_rx_ftr *ftr;
uint32_t sync_offset_us;
struct node_rx_pdu *rx;
uint32_t interval_us;
uint16_t interval;
uint16_t handle;
uint32_t ret;
lll = &sync_iso->lll;
handle = ull_sync_iso_handle_get(sync_iso);
interval = sys_le16_to_cpu(big_info->iso_interval);
interval_us = interval * CONN_INT_UNIT_US;
/* TODO: Populate LLL with information from the BIGINFO */
rx = (void *)sync_iso->sync->iso.node_rx_estab;
rx->hdr.type = NODE_RX_TYPE_SYNC_ISO;
rx->hdr.handle = handle;
rx->hdr.rx_ftr.param = sync_iso;
se = (void *)rx->pdu;
se->status = BT_HCI_ERR_SUCCESS;
se->interval = interval;
ll_rx_put(rx->hdr.link, rx);
ll_rx_sched();
ftr = &node_rx->rx_ftr;
/* TODO: Calculate offset */
sync_offset_us = 0;
ticks_slot_offset = 0;
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
ticks_slot_overhead = ticks_slot_offset;
} else {
ticks_slot_overhead = 0U;
}
ret = ticker_start(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH,
(TICKER_ID_SCAN_SYNC_ISO_BASE + handle),
ftr->ticks_anchor - ticks_slot_offset,
HAL_TICKER_US_TO_TICKS(sync_offset_us),
HAL_TICKER_US_TO_TICKS(interval_us),
HAL_TICKER_REMAINDER(interval_us),
TICKER_NULL_LAZY,
ticks_slot_overhead,
ticker_cb, sync_iso, ticker_op_cb, (void *)__LINE__);
LL_ASSERT((ret == TICKER_STATUS_SUCCESS) ||
(ret == TICKER_STATUS_BUSY));
}
static int init_reset(void)
{
/* Initialize sync pool. */
mem_init(ll_sync_iso_pool, sizeof(struct ll_sync_iso),
sizeof(ll_sync_iso_pool) / sizeof(struct ll_sync_iso),
&sync_iso_free);
return 0;
}
static inline struct ll_sync_iso *sync_iso_acquire(void)
{
return mem_acquire(&sync_iso_free);
}
static void ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
uint32_t remainder, uint16_t lazy, uint8_t force,
void *param)
{
/* TODO: Implement LLL handling */
#if 0
static memq_link_t link;
static struct mayfly mfy = {0, 0, &link, NULL, lll_sync_prepare};
static struct lll_prepare_param p;
struct ll_sync_set *sync = param;
struct lll_sync *lll;
uint32_t ret;
uint8_t ref;
DEBUG_RADIO_PREPARE_O(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_O(1);
#endif
}
static void ticker_op_cb(uint32_t status, void *param)
{
ARG_UNUSED(param);
LL_ASSERT(status == TICKER_STATUS_SUCCESS);
}