/* * Copyright (c) 2021 Demant * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include "util/mem.h" #include "util/memq.h" #include "util/mayfly.h" #include "ticker/ticker.h" #include "lll.h" #include "lll_conn_iso.h" #include "ull_conn_iso_types.h" #include "ull_internal.h" #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) #define LOG_MODULE_NAME bt_ctlr_ull_conn_iso #include "common/log.h" #include "hal/debug.h" static struct ll_conn_iso_stream cis_pool[CONFIG_BT_CTLR_CONN_ISO_STREAMS]; static void *cis_free; static struct ll_conn_iso_group cig_pool[CONFIG_BT_CTLR_CONN_ISO_GROUPS]; static void *cig_free; static int init_reset(void); struct ll_conn_iso_group *ll_conn_iso_group_acquire(void) { return mem_acquire(&cig_free); } void ll_conn_iso_group_release(struct ll_conn_iso_group *cig) { mem_release(cig, &cig_free); } uint16_t ll_conn_iso_group_handle_get(struct ll_conn_iso_group *cig) { return mem_index_get(cig, cig_pool, sizeof(struct ll_conn_iso_group)); } struct ll_conn_iso_group *ll_conn_iso_group_get(uint16_t handle) { return mem_get(cig_pool, sizeof(struct ll_conn_iso_group), handle); } struct ll_conn_iso_group *ll_conn_iso_group_get_by_id(uint8_t id) { struct ll_conn_iso_group *cig; for (int h = 0; h < CONFIG_BT_CTLR_CONN_ISO_GROUPS; h++) { cig = ll_conn_iso_group_get(h); if (id == cig->cig_id) { return cig; } } return NULL; } struct ll_conn_iso_stream *ll_conn_iso_stream_acquire(void) { return mem_acquire(&cis_free); } void ll_conn_iso_stream_release(struct ll_conn_iso_stream *cis) { mem_release(cis, &cig_free); } uint16_t ll_conn_iso_stream_handle_get(struct ll_conn_iso_stream *cis) { return mem_index_get(cis, cis_pool, sizeof(struct ll_conn_iso_stream)) + LL_CIS_HANDLE_BASE; } struct ll_conn_iso_stream *ll_conn_iso_stream_get(uint16_t handle) { return mem_get(cis_pool, sizeof(struct ll_conn_iso_stream), handle - LL_CIS_HANDLE_BASE); } struct ll_conn_iso_stream *ll_iso_stream_connected_get(uint16_t handle) { struct ll_conn_iso_stream *cis; if (handle >= CONFIG_BT_CTLR_CONN_ISO_STREAMS + LL_CIS_HANDLE_BASE) { return NULL; } cis = ll_conn_iso_stream_get(handle); if (cis->lll.handle != handle) { return NULL; } return cis; } int ull_conn_iso_init(void) { return init_reset(); } int ull_conn_iso_reset(void) { return init_reset(); } static int init_reset(void) { int err; /* Initialize CIS pool */ mem_init(cis_pool, sizeof(struct ll_conn_iso_stream), sizeof(cis_pool) / sizeof(struct ll_conn_iso_stream), &cis_free); /* Initialize CIG pool */ mem_init(cig_pool, sizeof(struct ll_conn_iso_group), sizeof(cig_pool) / sizeof(struct ll_conn_iso_group), &cig_free); for (int h = 0; h < CONFIG_BT_CTLR_CONN_ISO_GROUPS; h++) { ll_conn_iso_group_get(h)->cig_id = 0xFF; } /* Initialize LLL */ err = lll_conn_iso_init(); if (err) { return err; } return 0; } static void ticker_update_cig_op_cb(uint32_t status, void *param) { /* CIG drift compensation succeeds, or it fails in a race condition * when disconnecting (race between ticker_update and ticker_stop * calls). TODO: Are the race-checks needed? */ LL_ASSERT(status == TICKER_STATUS_SUCCESS || param == ull_update_mark_get() || param == ull_disable_mark_get()); } void ull_conn_iso_cis_established(struct ll_conn_iso_stream *cis) { struct node_rx_conn_iso_estab *est; struct node_rx_pdu *node_rx; node_rx = ull_pdu_rx_alloc(); if (!node_rx) { /* No node available - try again later */ return; } /* TODO: Send CIS_ESTABLISHED with status != 0 in error scenarios */ node_rx->hdr.type = NODE_RX_TYPE_CIS_ESTABLISHED; node_rx->hdr.handle = 0xFFFF; node_rx->hdr.rx_ftr.param = cis; est = (void *)node_rx->pdu; est->status = 0; est->cis_handle = sys_le16_to_cpu(cis->lll.handle); ll_rx_put(node_rx->hdr.link, node_rx); ll_rx_sched(); cis->established = 1; } void ull_conn_iso_done(struct node_rx_event_done *done) { struct lll_conn_iso_group *lll = (void *)HDR_ULL2LLL(done->param); struct ll_conn_iso_group *cig = (void *)HDR_LLL2EVT(lll); uint32_t ticks_drift_minus; uint32_t ticks_drift_plus; /* Skip if CIG terminated by local host */ if (unlikely(lll->handle == 0xFFFF)) { return; } ticks_drift_plus = 0; ticks_drift_minus = 0; if (done->extra.trx_cnt) { if (IS_ENABLED(CONFIG_BT_CTLR_PERIPHERAL_ISO) && lll->role) { ull_drift_ticks_get(done, &ticks_drift_plus, &ticks_drift_minus); } } /* Update CIG ticker to compensate for drift */ if (ticks_drift_plus || ticks_drift_minus) { uint8_t ticker_id = TICKER_ID_CONN_ISO_BASE + ll_conn_iso_group_handle_get(cig); struct ll_conn *conn = lll->hdr.parent; uint32_t ticker_status; ticker_status = ticker_update(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH, ticker_id, ticks_drift_plus, ticks_drift_minus, 0, 0, TICKER_NULL_LAZY, 0, ticker_update_cig_op_cb, cig); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY) || ((void *)conn == ull_disable_mark_get())); } }