LL_CIS_IND starts a ticker for the created CIG, using the event_counter and offset provided. Ticker generates callbacks to lll_peripheral_iso_prepare. Event done with ISO (extra) type is demuxed and done handled for CIG including ticker update with drift compensation. TODO: Handle multiple CISes as well as pause/resume and scheduling latency. Signed-off-by: Morten Priess <mtpr@oticon.com>
225 lines
5.1 KiB
C
225 lines
5.1 KiB
C
/*
|
|
* Copyright (c) 2021 Demant
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr.h>
|
|
#include <sys/byteorder.h>
|
|
|
|
#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()));
|
|
}
|
|
}
|