Bluetooth: controller: Implement CIS Central in ULL
- Add CIS Create policy config choices - Implemented CIS Central in ULL following proposed setup/commit flow - Full support for HCI_LE_Set_CIG_Parameters_Test - Extend ull_conn_iso_start and ull_conn_iso_ticker_cb to handle central role - Partial support for HCI_LE_Set_CIG_Parameters. TODOs: * Drop suggested retransmissions if Max_Transmission_Latency is exceeded * Calculate ISO interval based on policy Signed-off-by: Morten Priess <mtpr@oticon.com>
This commit is contained in:
parent
07acdc9650
commit
745bf7297e
11 changed files with 718 additions and 84 deletions
|
@ -853,6 +853,28 @@ config BT_CTLR_CONN_ISO_STREAMS_MAX_NSE
|
|||
help
|
||||
Maximum number of CIS subevents.
|
||||
|
||||
choice
|
||||
prompt "CIS Creation Policy Selection"
|
||||
default BT_CTLR_CONN_ISO_RELIABILITY_POLICY
|
||||
|
||||
config BT_CTLR_CONN_ISO_RELIABILITY_POLICY
|
||||
bool "CIS creation policy for reliability"
|
||||
depends on BT_CTLR_CENTRAL_ISO
|
||||
help
|
||||
Select this option to use reliability policy for CIS creation. This
|
||||
favors a CIS layout/configuration which utilizes the full range of the
|
||||
Max_Transmission_Latency for maximum retransmission and payload
|
||||
recovery.
|
||||
|
||||
config BT_CTLR_CONN_ISO_LOW_LATENCY_POLICY
|
||||
bool "CIS creation policy for low latency"
|
||||
depends on BT_CTLR_CENTRAL_ISO
|
||||
help
|
||||
Select this option to use low latency policy for CIS creation. This
|
||||
favors a CIS layout/configuration which compacts payload transmission
|
||||
for lowest possible latency.
|
||||
endchoice
|
||||
|
||||
config BT_CTLR_ISO
|
||||
bool
|
||||
default BT_CTLR_BROADCAST_ISO || BT_CTLR_CONN_ISO
|
||||
|
|
|
@ -34,6 +34,7 @@ struct lll_conn_iso_stream {
|
|||
uint8_t nesn:1; /* Next expected sequence number */
|
||||
uint8_t cie:1; /* Close isochronous event */
|
||||
uint8_t flushed:1; /* 1 if CIS LLL has been flushed */
|
||||
uint8_t active:1; /* 1 if CIS LLL is active */
|
||||
uint8_t datapath_ready_rx:1;/* 1 if datapath for RX is ready */
|
||||
|
||||
/* Resumption information */
|
||||
|
@ -73,6 +74,4 @@ struct lll_conn_iso_group {
|
|||
|
||||
int lll_conn_iso_init(void);
|
||||
int lll_conn_iso_reset(void);
|
||||
void lll_conn_iso_done(struct lll_conn_iso_group *cig, uint8_t trx_cnt,
|
||||
uint16_t prog_to_anchor_us, uint8_t mic_state);
|
||||
void lll_conn_iso_flush(uint16_t handle, struct lll_conn_iso_stream *lll);
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/toolchain.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <soc.h>
|
||||
|
||||
#include "hal/cpu.h"
|
||||
#include "hal/ticker.h"
|
||||
|
||||
#include "util/memq.h"
|
||||
#include "util/mfifo.h"
|
||||
|
||||
#include "pdu.h"
|
||||
|
||||
#include "lll.h"
|
||||
#include "lll_conn_iso.h"
|
||||
#include "lll_peripheral_iso.h"
|
||||
|
||||
#define LOG_MODULE_NAME bt_ctlr_lll_peripheral_iso
|
||||
#include "common/log.h"
|
||||
#include "hal/debug.h"
|
||||
|
||||
int lll_central_iso_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lll_central_iso_reset(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lll_central_iso_prepare(void *param)
|
||||
{
|
||||
ARG_UNUSED(param);
|
||||
}
|
|
@ -80,3 +80,8 @@
|
|||
#define EVENT_TICKS_TO_US_FRAC(ticks) HAL_TICKER_TICKS_TO_US(ticks)
|
||||
#define EVENT_US_FRAC_TO_TICKS(us_frac) HAL_TICKER_US_TO_TICKS(us_frac)
|
||||
#define EVENT_US_FRAC_TO_REMAINDER(us_frac) HAL_TICKER_REMAINDER(us_frac)
|
||||
|
||||
/* Time needed to set up a CIS from ACL instant to prepare (incl. radio). Used
|
||||
* for CIS_Offset_Min.
|
||||
*/
|
||||
#define EVENT_OVERHEAD_CIS_SETUP_US MAX(EVENT_OVERHEAD_START_US, 500U)
|
||||
|
|
|
@ -72,6 +72,10 @@ if(CONFIG_BT_LL_SW_SPLIT)
|
|||
ll_sw/nordic/lll/lll_conn_iso.c
|
||||
)
|
||||
endif()
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_BT_CTLR_CENTRAL_ISO
|
||||
ll_sw/nordic/lll/lll_central_iso.c
|
||||
)
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_BT_CTLR_PERIPHERAL_ISO
|
||||
ll_sw/nordic/lll/lll_peripheral_iso.c
|
||||
|
|
|
@ -335,6 +335,11 @@
|
|||
((enc) ? (PDU_MIC_SIZE) : 0), \
|
||||
(phy), (s8))
|
||||
|
||||
#define PDU_CIS_MAX_US(octets, enc, phy) PDU_MAX_US((octets), \
|
||||
((enc) ? \
|
||||
(PDU_MIC_SIZE) : 0), \
|
||||
(phy))
|
||||
|
||||
struct pdu_adv_adv_ind {
|
||||
uint8_t addr[BDADDR_SIZE];
|
||||
uint8_t data[PDU_AC_LEG_DATA_SIZE_MAX];
|
||||
|
|
|
@ -5,29 +5,74 @@
|
|||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/bluetooth/buf.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/bluetooth/iso.h>
|
||||
|
||||
#include "util/memq.h"
|
||||
#include "util/mayfly.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include "hal/ccm.h"
|
||||
#include "hal/ticker.h"
|
||||
|
||||
#include "ticker/ticker.h"
|
||||
|
||||
#include "pdu.h"
|
||||
|
||||
#include "lll.h"
|
||||
#include "lll/lll_vendor.h"
|
||||
#include "lll_conn.h"
|
||||
#include "lll_conn_iso.h"
|
||||
#include "lll_clock.h"
|
||||
|
||||
#include "isoal.h"
|
||||
#include "ull_iso_types.h"
|
||||
#include "ull_tx_queue.h"
|
||||
#include "ull_internal.h"
|
||||
|
||||
#include "ull_conn_types.h"
|
||||
#include "ull_conn_internal.h"
|
||||
#include "ull_conn_iso_types.h"
|
||||
#include "ull_conn_iso_internal.h"
|
||||
#include "ull_llcp.h"
|
||||
#include "lll_central_iso.h"
|
||||
|
||||
#include "ll.h"
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
|
||||
#define LOG_MODULE_NAME bt_ctlr_ull_central_iso
|
||||
#include "common/log.h"
|
||||
#include "hal/debug.h"
|
||||
|
||||
/* Setup cache for CIG commit transaction */
|
||||
static struct {
|
||||
struct ll_conn_iso_group group;
|
||||
uint8_t c_ft;
|
||||
uint8_t p_ft;
|
||||
uint8_t cis_idx;
|
||||
struct ll_conn_iso_stream stream[CONFIG_BT_CTLR_CONN_ISO_STREAMS_PER_GROUP];
|
||||
} ll_iso_setup;
|
||||
|
||||
uint8_t ll_cig_parameters_open(uint8_t cig_id,
|
||||
uint32_t c_interval, uint32_t p_interval,
|
||||
uint8_t sca, uint8_t packing, uint8_t framing,
|
||||
uint16_t c_latency, uint16_t p_latency,
|
||||
uint8_t num_cis)
|
||||
{
|
||||
ARG_UNUSED(cig_id);
|
||||
ARG_UNUSED(c_interval);
|
||||
ARG_UNUSED(p_interval);
|
||||
ARG_UNUSED(sca);
|
||||
ARG_UNUSED(packing);
|
||||
ARG_UNUSED(framing);
|
||||
ARG_UNUSED(c_latency);
|
||||
ARG_UNUSED(p_latency);
|
||||
ARG_UNUSED(num_cis);
|
||||
memset(&ll_iso_setup, 0, sizeof(ll_iso_setup));
|
||||
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
ll_iso_setup.group.cig_id = cig_id;
|
||||
ll_iso_setup.group.c_sdu_interval = c_interval;
|
||||
ll_iso_setup.group.p_sdu_interval = p_interval;
|
||||
ll_iso_setup.group.c_latency = c_latency * 1000;
|
||||
ll_iso_setup.group.p_latency = p_latency * 1000;
|
||||
ll_iso_setup.group.cis_count = num_cis;
|
||||
ll_iso_setup.group.central.sca = sca;
|
||||
ll_iso_setup.group.central.packing = packing;
|
||||
ll_iso_setup.group.central.framing = framing;
|
||||
|
||||
return BT_HCI_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t ll_cis_parameters_set(uint8_t cis_id,
|
||||
|
@ -35,17 +80,33 @@ uint8_t ll_cis_parameters_set(uint8_t cis_id,
|
|||
uint8_t c_phy, uint8_t p_phy,
|
||||
uint8_t c_rtn, uint8_t p_rtn)
|
||||
{
|
||||
uint8_t cis_idx = ll_iso_setup.cis_idx;
|
||||
|
||||
ARG_UNUSED(cis_id);
|
||||
ARG_UNUSED(c_sdu);
|
||||
ARG_UNUSED(p_sdu);
|
||||
ARG_UNUSED(c_phy);
|
||||
ARG_UNUSED(p_phy);
|
||||
ARG_UNUSED(c_rtn);
|
||||
ARG_UNUSED(p_rtn);
|
||||
ARG_UNUSED(handle);
|
||||
if (cis_idx >= CONFIG_BT_CTLR_CONN_ISO_STREAMS_PER_GROUP) {
|
||||
return BT_HCI_ERR_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
memset(&ll_iso_setup.stream[cis_idx], 0, sizeof(struct ll_conn_iso_stream));
|
||||
|
||||
ll_iso_setup.stream[cis_idx].cis_id = cis_id;
|
||||
ll_iso_setup.stream[cis_idx].c_max_sdu = c_sdu;
|
||||
ll_iso_setup.stream[cis_idx].p_max_sdu = p_sdu;
|
||||
ll_iso_setup.stream[cis_idx].lll.tx.phy = c_phy;
|
||||
ll_iso_setup.stream[cis_idx].lll.rx.phy = p_phy;
|
||||
ll_iso_setup.stream[cis_idx].central.c_rtn = c_rtn;
|
||||
ll_iso_setup.stream[cis_idx].central.p_rtn = p_rtn;
|
||||
ll_iso_setup.cis_idx++;
|
||||
|
||||
return BT_HCI_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void ll_cis_create(uint16_t cis_handle, uint16_t acl_handle)
|
||||
{
|
||||
/* Handles have been verified prior to calling this function */
|
||||
(void)ull_cp_cis_create(
|
||||
ll_connected_get(acl_handle),
|
||||
ll_conn_iso_stream_get(cis_handle));
|
||||
}
|
||||
|
||||
uint8_t ll_cig_parameters_test_open(uint8_t cig_id,
|
||||
|
@ -59,68 +120,414 @@ uint8_t ll_cig_parameters_test_open(uint8_t cig_id,
|
|||
uint8_t framing,
|
||||
uint8_t num_cis)
|
||||
{
|
||||
ARG_UNUSED(cig_id);
|
||||
ARG_UNUSED(c_interval);
|
||||
ARG_UNUSED(p_interval);
|
||||
ARG_UNUSED(c_ft);
|
||||
ARG_UNUSED(p_ft);
|
||||
ARG_UNUSED(iso_interval);
|
||||
ARG_UNUSED(sca);
|
||||
ARG_UNUSED(packing);
|
||||
ARG_UNUSED(framing);
|
||||
ARG_UNUSED(num_cis);
|
||||
memset(&ll_iso_setup, 0, sizeof(ll_iso_setup));
|
||||
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
ll_iso_setup.group.cig_id = cig_id;
|
||||
ll_iso_setup.group.c_sdu_interval = c_interval;
|
||||
ll_iso_setup.group.p_sdu_interval = p_interval;
|
||||
ll_iso_setup.group.iso_interval = iso_interval;
|
||||
ll_iso_setup.group.cis_count = num_cis;
|
||||
ll_iso_setup.group.central.sca = sca;
|
||||
ll_iso_setup.group.central.packing = packing;
|
||||
ll_iso_setup.group.central.framing = framing;
|
||||
ll_iso_setup.group.central.test = 1U;
|
||||
|
||||
/* TODO: Perhaps move FT to LLL CIG */
|
||||
ll_iso_setup.c_ft = c_ft;
|
||||
ll_iso_setup.p_ft = p_ft;
|
||||
|
||||
return BT_HCI_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t ll_cis_parameters_test_set(uint8_t cis_id, uint8_t nse,
|
||||
uint8_t ll_cis_parameters_test_set(uint8_t cis_id, uint8_t nse,
|
||||
uint16_t c_sdu, uint16_t p_sdu,
|
||||
uint16_t c_pdu, uint16_t p_pdu,
|
||||
uint8_t c_phy, uint8_t p_phy,
|
||||
uint8_t c_bn, uint8_t p_bn)
|
||||
{
|
||||
ARG_UNUSED(cis_id);
|
||||
ARG_UNUSED(c_sdu);
|
||||
ARG_UNUSED(p_sdu);
|
||||
ARG_UNUSED(c_pdu);
|
||||
ARG_UNUSED(p_pdu);
|
||||
ARG_UNUSED(c_phy);
|
||||
ARG_UNUSED(p_phy);
|
||||
ARG_UNUSED(c_bn);
|
||||
ARG_UNUSED(p_bn);
|
||||
ARG_UNUSED(handle);
|
||||
uint8_t cis_idx = ll_iso_setup.cis_idx;
|
||||
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
if (cis_idx >= CONFIG_BT_CTLR_CONN_ISO_STREAMS_PER_GROUP) {
|
||||
return BT_HCI_ERR_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
memset(&ll_iso_setup.stream[cis_idx], 0, sizeof(struct ll_conn_iso_stream));
|
||||
|
||||
ll_iso_setup.stream[cis_idx].cis_id = cis_id;
|
||||
ll_iso_setup.stream[cis_idx].c_max_sdu = c_sdu;
|
||||
ll_iso_setup.stream[cis_idx].p_max_sdu = p_sdu;
|
||||
ll_iso_setup.stream[cis_idx].lll.num_subevents = nse;
|
||||
ll_iso_setup.stream[cis_idx].lll.tx.max_octets = c_bn ? c_pdu : 0;
|
||||
ll_iso_setup.stream[cis_idx].lll.rx.max_octets = p_bn ? p_pdu : 0;
|
||||
ll_iso_setup.stream[cis_idx].lll.tx.phy = c_phy;
|
||||
ll_iso_setup.stream[cis_idx].lll.rx.phy = p_phy;
|
||||
ll_iso_setup.stream[cis_idx].lll.tx.burst_number = c_bn;
|
||||
ll_iso_setup.stream[cis_idx].lll.rx.burst_number = p_bn;
|
||||
ll_iso_setup.cis_idx++;
|
||||
|
||||
return BT_HCI_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* TODO:
|
||||
* - Drop retransmissions to stay within Max_Transmission_Latency instead of asserting
|
||||
* - Calculate ISO_Interval to allow SDU_Interval < ISO_Interval
|
||||
*/
|
||||
uint8_t ll_cig_parameters_commit(uint8_t cig_id)
|
||||
{
|
||||
ARG_UNUSED(cig_id);
|
||||
struct ll_conn_iso_stream *cis;
|
||||
struct ll_conn_iso_group *cig;
|
||||
uint32_t iso_interval_us;
|
||||
uint32_t cig_sync_delay;
|
||||
uint32_t max_se_length;
|
||||
uint32_t c_max_latency;
|
||||
uint32_t p_max_latency;
|
||||
uint16_t handle_iter;
|
||||
uint8_t cis_count;
|
||||
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
/* Intermediate subevent data */
|
||||
struct {
|
||||
uint32_t length;
|
||||
uint8_t total_count;
|
||||
} se[CONFIG_BT_CTLR_CONN_ISO_STREAMS_PER_GROUP];
|
||||
|
||||
/* If CIG already exists, controller and host are not in sync */
|
||||
cig = ll_conn_iso_group_get_by_id(cig_id);
|
||||
LL_ASSERT(!cig);
|
||||
|
||||
/* CIG does not exist - create it */
|
||||
cig = ll_conn_iso_group_acquire();
|
||||
if (!cig) {
|
||||
/* No space for new CIG */
|
||||
return BT_HCI_ERR_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
/* Transfer parameters from update cache and clear LLL fields */
|
||||
memcpy(cig, &ll_iso_setup.group, sizeof(struct ll_conn_iso_group));
|
||||
|
||||
/* Setup LLL parameters */
|
||||
cig->lll.handle = ll_conn_iso_group_handle_get(cig);
|
||||
cig->lll.role = BT_HCI_ROLE_CENTRAL;
|
||||
cig->lll.resume_cis = LLL_HANDLE_INVALID;
|
||||
|
||||
if (!cig->central.test) {
|
||||
/* TODO: Calculate ISO_Interval based on SDU_Interval and Max_SDU vs Max_PDU,
|
||||
* taking the policy into consideration. It may also be intersting to select an
|
||||
* ISO_Interval which is less likely to collide with other connections.
|
||||
* For instance:
|
||||
*
|
||||
* SDU_Interval ISO_Interval Max_SDU Max_SDU Collision risk (10 ms)
|
||||
* ------------------------------------------------------------------------
|
||||
* 10 ms 10 ms 40 40 100%
|
||||
* 10 ms 12.5 ms 40 50 25%
|
||||
*/
|
||||
iso_interval_us = cig->c_sdu_interval;
|
||||
cig->iso_interval = ceiling_fraction(iso_interval_us, ISO_INT_UNIT_US);
|
||||
} else {
|
||||
iso_interval_us = cig->iso_interval * ISO_INT_UNIT_US;
|
||||
}
|
||||
|
||||
ull_hdr_init(&cig->ull);
|
||||
lll_hdr_init(&cig->lll, cig);
|
||||
|
||||
max_se_length = 0;
|
||||
cis_count = cig->cis_count;
|
||||
|
||||
/* 1) Acquire CIS instances and initialize instance data.
|
||||
* 2) Calculate SE_Length for each CIS and store the largest
|
||||
* 3) Calculate BN
|
||||
* 4) Calculate total number of subevents needed to transfer payloads
|
||||
*
|
||||
* Sequential Interleaved
|
||||
* CIS0 ___█_█_█_____________█_ ___█___█___█_________█_
|
||||
* CIS1 _________█_█_█_________ _____█___█___█_________
|
||||
* CIS_Sub_Interval |.| |...|
|
||||
* CIG_Sync_Delay |............| |............|
|
||||
* CIS_Sync_Delay 0 |............| |............|
|
||||
* CIS_Sync_Delay 1 |......| |..........|
|
||||
* ISO_Interval |.................|.. |.................|..
|
||||
*/
|
||||
for (uint8_t i = 0; i < cis_count; i++) {
|
||||
uint32_t mpt_c;
|
||||
uint32_t mpt_p;
|
||||
|
||||
const bool tx = cig->c_sdu_interval > 0;
|
||||
const bool rx = cig->p_sdu_interval > 0;
|
||||
|
||||
/* Acquire new CIS */
|
||||
cis = ll_conn_iso_stream_acquire();
|
||||
if (cis == NULL) {
|
||||
/* No space for new CIS */
|
||||
return BT_HCI_ERR_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
/* Transfer parameters from update cache */
|
||||
memcpy(cis, &ll_iso_setup.stream[i], sizeof(struct ll_conn_iso_stream));
|
||||
cis->group = cig;
|
||||
|
||||
cis->lll.handle = ll_conn_iso_stream_handle_get(cis);
|
||||
|
||||
if (cig->central.test) {
|
||||
cis->lll.tx.flush_timeout = ll_iso_setup.c_ft;
|
||||
cis->lll.rx.flush_timeout = ll_iso_setup.p_ft;
|
||||
} else {
|
||||
LL_ASSERT(iso_interval_us >= cig->c_sdu_interval);
|
||||
|
||||
if (cis->framed) {
|
||||
cis->lll.tx.max_octets =
|
||||
ceiling_fraction(cis->c_max_sdu * cig->c_sdu_interval,
|
||||
iso_interval_us);
|
||||
cis->lll.rx.max_octets =
|
||||
ceiling_fraction(cis->p_max_sdu * cig->c_sdu_interval,
|
||||
iso_interval_us);
|
||||
} else {
|
||||
/* For unframed, ISO_Interval must be N x SDU_Interval */
|
||||
LL_ASSERT(iso_interval_us % cig->c_sdu_interval == 0);
|
||||
|
||||
/* Use Max_PDU = MIN(<buffer_size>, Max_SDU) */
|
||||
cis->lll.tx.max_octets = MIN(CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE,
|
||||
cis->c_max_sdu);
|
||||
cis->lll.rx.max_octets = MIN(251, cis->p_max_sdu);
|
||||
}
|
||||
|
||||
/* BN >= Max_SDU/Max_PDU * ISO_Interval/SDU_Interval */
|
||||
cis->lll.tx.burst_number = tx ?
|
||||
ceiling_fraction(cis->c_max_sdu * iso_interval_us,
|
||||
cis->lll.tx.max_octets * cig->c_sdu_interval) : 0;
|
||||
cis->lll.rx.burst_number = rx ?
|
||||
ceiling_fraction(cis->p_max_sdu * iso_interval_us,
|
||||
cis->lll.rx.max_octets * cig->p_sdu_interval) : 0;
|
||||
}
|
||||
|
||||
/* Calculate SE_Length */
|
||||
mpt_c = PDU_CIS_MAX_US(tx ? cis->lll.tx.max_octets : 0, tx ? 1 : 0,
|
||||
cis->lll.tx.phy);
|
||||
mpt_p = PDU_CIS_MAX_US(rx ? cis->lll.rx.max_octets : 0, rx ? 1 : 0,
|
||||
cis->lll.rx.phy);
|
||||
|
||||
se[i].length = mpt_c + EVENT_IFS_US + mpt_p + EVENT_MSS_US;
|
||||
max_se_length = MAX(max_se_length, se[i].length);
|
||||
|
||||
/* Total number of subevents needed */
|
||||
se[i].total_count = MAX((cis->central.c_rtn + 1) * cis->lll.tx.burst_number,
|
||||
(cis->central.p_rtn + 1) * cis->lll.rx.burst_number);
|
||||
|
||||
/* Initialize TX link */
|
||||
cis->lll.link_tx_free = &cis->lll.link_tx;
|
||||
|
||||
memq_init(cis->lll.link_tx_free, &cis->lll.memq_tx.head, &cis->lll.memq_tx.tail);
|
||||
cis->lll.link_tx_free = NULL;
|
||||
}
|
||||
|
||||
handle_iter = UINT16_MAX;
|
||||
uint32_t total_time = 0;
|
||||
|
||||
/* 1) Prepare calculation of the flush timeout by adding up the total time needed to
|
||||
* transfer all payloads, including retransmissions.
|
||||
*/
|
||||
for (uint8_t i = 0; i < cis_count; i++) {
|
||||
cis = ll_conn_iso_stream_get_by_group(cig, &handle_iter);
|
||||
|
||||
if (cig->central.packing == BT_ISO_PACKING_SEQUENTIAL) {
|
||||
/* Sequential CISes - add up the duration and set individual
|
||||
* subinterval.
|
||||
*/
|
||||
total_time += se[i].total_count * se[i].length;
|
||||
} else {
|
||||
/* Interleaved CISes - find the largest total duration and
|
||||
* set all subintervals to the largest SE_Length of any CIS x
|
||||
* the number of interleaved CISes.
|
||||
*/
|
||||
total_time = MAX(total_time, se[i].total_count * cis->lll.sub_interval +
|
||||
(i * cis->lll.sub_interval / cis_count));
|
||||
}
|
||||
}
|
||||
|
||||
handle_iter = UINT16_MAX;
|
||||
cig_sync_delay = 0;
|
||||
|
||||
/* 1) Calculate the flush timeout either by dividing the total time needed to transfer all,
|
||||
* payloads including retransmissions, and divide by the ISO_Interval (low latency
|
||||
* policy), or by dividing the Max_Transmission_Latency by the ISO_Interval (reliability
|
||||
* policy).
|
||||
* 2) Calculate the number of subevents (NSE) by distributing total number of subevents into
|
||||
* FT ISO_intervals.
|
||||
* 3) Calculate subinterval as either individual CIS subinterval (sequential), or the
|
||||
* largest SE_Length times number of CISes (interleaved). Min. subinterval is 400 us.
|
||||
* 4) Calculate CIG_Sync_Delay
|
||||
*/
|
||||
for (uint8_t i = 0; i < cis_count; i++) {
|
||||
cis = ll_conn_iso_stream_get_by_group(cig, &handle_iter);
|
||||
|
||||
if (!cig->central.test) {
|
||||
#if defined(CONFIG_BT_CTLR_CONN_ISO_LOW_LATENCY_POLICY)
|
||||
/* Use symmetric flush timeout */
|
||||
cis->lll.tx.flush_timeout = ceiling_fraction(total_time, iso_interval_us);
|
||||
cis->lll.rx.flush_timeout = cis->lll.tx.flush_timeout;
|
||||
|
||||
#elif defined(CONFIG_BT_CTLR_CONN_ISO_RELIABILITY_POLICY)
|
||||
cis->lll.tx.flush_timeout = ceiling_fraction(cig->c_latency,
|
||||
iso_interval_us);
|
||||
cis->lll.rx.flush_timeout = ceiling_fraction(cig->p_latency,
|
||||
iso_interval_us);
|
||||
#else
|
||||
LL_ASSERT(0);
|
||||
#endif
|
||||
cis->lll.num_subevents = ceiling_fraction(se[i].total_count,
|
||||
cis->lll.tx.flush_timeout);
|
||||
}
|
||||
|
||||
if (cig->central.packing == BT_ISO_PACKING_SEQUENTIAL) {
|
||||
/* Accumulate CIG sync delay for sequential CISes */
|
||||
cis->lll.sub_interval = MAX(400, se[i].length);
|
||||
cig_sync_delay += cis->lll.num_subevents * cis->lll.sub_interval;
|
||||
} else {
|
||||
/* For interleaved CISes, offset each CIS by a fraction of a subinterval,
|
||||
* positioning them evenly within the subinterval.
|
||||
*/
|
||||
cis->lll.sub_interval = MAX(400, cis_count * max_se_length);
|
||||
cig_sync_delay = MAX(cig_sync_delay,
|
||||
(cis->lll.num_subevents * cis->lll.sub_interval) +
|
||||
(i * cis->lll.sub_interval / cis_count));
|
||||
}
|
||||
}
|
||||
|
||||
cig->sync_delay = cig_sync_delay;
|
||||
|
||||
handle_iter = UINT16_MAX;
|
||||
c_max_latency = 0;
|
||||
p_max_latency = 0;
|
||||
|
||||
/* 1) Calculate transport latencies for each CIS and validate against Max_Transport_Latency.
|
||||
* 2) Lay out CISes by updating CIS_Sync_Delay, distributing according to the packing.
|
||||
*/
|
||||
for (uint8_t i = 0; i < cis_count; i++) {
|
||||
uint32_t c_latency;
|
||||
uint32_t p_latency;
|
||||
|
||||
cis = ll_conn_iso_stream_get_by_group(cig, &handle_iter);
|
||||
|
||||
if (cis->framed) {
|
||||
/* Transport_Latency = CIG_Sync_Delay + FT x ISO_Interval + SDU_Interval */
|
||||
c_latency = cig->sync_delay +
|
||||
(cis->lll.tx.flush_timeout * iso_interval_us) +
|
||||
cig->c_sdu_interval;
|
||||
p_latency = cig->sync_delay +
|
||||
(cis->lll.rx.flush_timeout * iso_interval_us) +
|
||||
cig->p_sdu_interval;
|
||||
|
||||
} else {
|
||||
/* Transport_Latency = CIG_Sync_Delay + FT x ISO_Interval - SDU_Interval */
|
||||
c_latency = cig->sync_delay +
|
||||
(cis->lll.tx.flush_timeout * iso_interval_us) -
|
||||
cig->c_sdu_interval;
|
||||
p_latency = cig->sync_delay +
|
||||
(cis->lll.rx.flush_timeout * iso_interval_us) -
|
||||
cig->p_sdu_interval;
|
||||
}
|
||||
|
||||
if (!cig->central.test) {
|
||||
/* Make sure specified Max_Transport_Latency is not exceeded */
|
||||
LL_ASSERT(c_latency <= cig->c_latency);
|
||||
LL_ASSERT(p_latency <= cig->p_latency);
|
||||
}
|
||||
|
||||
c_max_latency = MAX(c_max_latency, c_latency);
|
||||
p_max_latency = MAX(p_max_latency, p_latency);
|
||||
|
||||
if (cig->central.packing == BT_ISO_PACKING_SEQUENTIAL) {
|
||||
/* Distribute CISes sequentially */
|
||||
cis->sync_delay = cig_sync_delay;
|
||||
cig_sync_delay -= cis->lll.num_subevents * cis->lll.sub_interval;
|
||||
} else {
|
||||
/* Distribute CISes interleaved */
|
||||
cis->sync_delay = cig_sync_delay;
|
||||
cig_sync_delay -= (cis->lll.sub_interval / cis_count);
|
||||
}
|
||||
|
||||
if (cis->lll.num_subevents <= 1) {
|
||||
cis->lll.sub_interval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update actual latency */
|
||||
cig->c_latency = c_max_latency;
|
||||
cig->p_latency = p_max_latency;
|
||||
|
||||
cig->lll.num_cis = cis_count;
|
||||
|
||||
return BT_HCI_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t ll_cig_remove(uint8_t cig_id)
|
||||
{
|
||||
ARG_UNUSED(cig_id);
|
||||
struct ll_conn_iso_stream *cis;
|
||||
struct ll_conn_iso_group *cig;
|
||||
uint16_t handle_iter;
|
||||
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
cig = ll_conn_iso_group_get_by_id(cig_id);
|
||||
if (!cig) {
|
||||
/* Unknown CIG id */
|
||||
return BT_HCI_ERR_UNKNOWN_CONN_ID;
|
||||
}
|
||||
|
||||
if (cig->started) {
|
||||
/* CIG is in active state */
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
|
||||
handle_iter = UINT16_MAX;
|
||||
for (int i = 0; i < cig->cis_count; i++) {
|
||||
struct ll_conn *conn;
|
||||
|
||||
cis = ll_conn_iso_stream_get_by_group(cig, &handle_iter);
|
||||
conn = ll_connected_get(cis->lll.acl_handle);
|
||||
|
||||
if (conn) {
|
||||
if (ull_lp_cc_is_active(conn)) {
|
||||
/* CIG creation is ongoing */
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* CIG exists and is not active */
|
||||
handle_iter = UINT16_MAX;
|
||||
for (int i = 0; i < cig->cis_count; i++) {
|
||||
cis = ll_conn_iso_stream_get_by_group(cig, &handle_iter);
|
||||
|
||||
/* Remove data path and ISOAL sink/source associated with this CIS
|
||||
* for both directions.
|
||||
*/
|
||||
ll_remove_iso_path(cis->lll.handle, BT_HCI_DATAPATH_DIR_CTLR_TO_HOST);
|
||||
ll_remove_iso_path(cis->lll.handle, BT_HCI_DATAPATH_DIR_HOST_TO_CTLR);
|
||||
|
||||
ll_conn_iso_stream_release(cis);
|
||||
}
|
||||
|
||||
ll_conn_iso_group_release(cig);
|
||||
|
||||
return BT_HCI_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t ll_cis_create_check(uint16_t cis_handle, uint16_t acl_handle)
|
||||
{
|
||||
ARG_UNUSED(cis_handle);
|
||||
ARG_UNUSED(acl_handle);
|
||||
struct ll_conn *conn;
|
||||
|
||||
conn = ll_connected_get(acl_handle);
|
||||
if (conn) {
|
||||
struct ll_conn_iso_stream *cis;
|
||||
|
||||
/* Verify handle validity and association */
|
||||
cis = ll_conn_iso_stream_get(cis_handle);
|
||||
if (cis->lll.handle == cis_handle && cis->lll.acl_handle == acl_handle) {
|
||||
return BT_HCI_ERR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
|
||||
void ll_cis_create(uint16_t cis_handle, uint16_t acl_handle)
|
||||
{
|
||||
ARG_UNUSED(cis_handle);
|
||||
ARG_UNUSED(acl_handle);
|
||||
}
|
||||
|
||||
int ull_central_iso_init(void)
|
||||
{
|
||||
return 0;
|
||||
|
@ -130,3 +537,96 @@ int ull_central_iso_reset(void)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ull_central_iso_setup(uint16_t cis_handle,
|
||||
uint32_t *cig_sync_delay,
|
||||
uint32_t *cis_sync_delay,
|
||||
uint32_t *cis_offset_min,
|
||||
uint32_t *cis_offset_max,
|
||||
uint16_t *conn_event_count,
|
||||
uint8_t *access_addr)
|
||||
{
|
||||
struct ll_conn_iso_stream *cis;
|
||||
struct ll_conn_iso_group *cig;
|
||||
struct ll_conn *conn;
|
||||
uint16_t handle_iter;
|
||||
uint32_t cis_offset;
|
||||
uint16_t instant;
|
||||
int err;
|
||||
|
||||
cis = ll_conn_iso_stream_get(cis_handle);
|
||||
if (!cis) {
|
||||
return BT_HCI_ERR_UNSPECIFIED;
|
||||
}
|
||||
|
||||
cig = cis->group;
|
||||
if (!cig) {
|
||||
return BT_HCI_ERR_UNSPECIFIED;
|
||||
}
|
||||
|
||||
conn = ll_conn_get(cis->lll.acl_handle);
|
||||
instant = MAX(*conn_event_count, ull_conn_event_counter(conn) + 1);
|
||||
|
||||
handle_iter = UINT16_MAX;
|
||||
cis_offset = *cis_offset_min;
|
||||
|
||||
/* Calculate offset for CIS */
|
||||
for (uint8_t i = 0; i < cig->cis_count; i++) {
|
||||
struct ll_conn_iso_stream *c;
|
||||
int16_t conn_events_since_ref;
|
||||
uint32_t iso_interval_us;
|
||||
uint32_t time_since_ref;
|
||||
uint32_t acl_offset;
|
||||
|
||||
c = ll_conn_iso_stream_get_by_group(cig, &handle_iter);
|
||||
if (c->cis_id != cis->cis_id && c->lll.active) {
|
||||
conn_events_since_ref = (int16_t)(instant - c->central.instant);
|
||||
LL_ASSERT(conn_events_since_ref > 0);
|
||||
|
||||
time_since_ref = c->offset + conn_events_since_ref * conn->lll.interval *
|
||||
CONN_INT_UNIT_US;
|
||||
iso_interval_us = cig->iso_interval * CONN_INT_UNIT_US;
|
||||
acl_offset = iso_interval_us - (time_since_ref % iso_interval_us);
|
||||
cis_offset = acl_offset + (cig->sync_delay - cis->sync_delay);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cis->offset = cis_offset;
|
||||
cis->central.instant = instant;
|
||||
cis->lll.event_count = -1;
|
||||
|
||||
/* Create access address */
|
||||
err = util_aa_le32(cis->lll.access_addr);
|
||||
LL_ASSERT(!err);
|
||||
|
||||
/* Transfer to caller */
|
||||
*cig_sync_delay = cig->sync_delay;
|
||||
*cis_sync_delay = cis->sync_delay;
|
||||
*cis_offset_min = cis->offset;
|
||||
memcpy(access_addr, cis->lll.access_addr, sizeof(cis->lll.access_addr));
|
||||
|
||||
*conn_event_count = instant;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t ull_central_iso_cis_offset_get(struct ll_conn_iso_stream *cis, uint32_t *cis_offset_min,
|
||||
uint32_t *cis_offset_max)
|
||||
{
|
||||
struct ll_conn_iso_group *cig;
|
||||
struct ll_conn *conn;
|
||||
|
||||
conn = ll_conn_get(cis->lll.acl_handle);
|
||||
cig = cis->group;
|
||||
|
||||
/* Provide CIS offset range
|
||||
* CIS_Offset_Max < (connInterval - (CIG_Sync_Delay + T_MSS))
|
||||
*/
|
||||
*cis_offset_max = (conn->lll.interval * CONN_INT_UNIT_US) - cig->sync_delay;
|
||||
*cis_offset_min = MAX(400, EVENT_OVERHEAD_CIS_SETUP_US);
|
||||
|
||||
cis->central.instant = ull_conn_event_counter(conn) + 3;
|
||||
|
||||
return cis->central.instant;
|
||||
}
|
||||
|
|
|
@ -82,6 +82,9 @@ struct ll_conn_iso_group *ll_conn_iso_group_acquire(void)
|
|||
|
||||
void ll_conn_iso_group_release(struct ll_conn_iso_group *cig)
|
||||
{
|
||||
cig->cig_id = 0xFF;
|
||||
cig->started = 0;
|
||||
|
||||
mem_release(cig, &cig_free);
|
||||
}
|
||||
|
||||
|
@ -489,7 +492,7 @@ void ull_conn_iso_ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
|
|||
void *param)
|
||||
{
|
||||
static memq_link_t link;
|
||||
static struct mayfly mfy = { 0, 0, &link, NULL, lll_peripheral_iso_prepare };
|
||||
static struct mayfly mfy = { 0, 0, &link, NULL, NULL };
|
||||
static struct lll_prepare_param p;
|
||||
struct ll_conn_iso_group *cig;
|
||||
struct ll_conn_iso_stream *cis;
|
||||
|
@ -520,7 +523,7 @@ void ull_conn_iso_ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
|
|||
* ull_peripheral_iso_start, which means that its ACL instant
|
||||
* has been reached, and offset calculated.
|
||||
*/
|
||||
if (cis->lll.handle != 0xFFFF) {
|
||||
if (cis->lll.handle != 0xFFFF && cis->lll.active) {
|
||||
cis->lll.event_count++;
|
||||
|
||||
leading_event_count = MAX(leading_event_count,
|
||||
|
@ -555,7 +558,16 @@ void ull_conn_iso_ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
|
|||
p.param = &cig->lll;
|
||||
mfy.param = &p;
|
||||
|
||||
if (cig->sca_update) {
|
||||
#if !defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
|
||||
mfy.fp = lll_central_iso_prepare;
|
||||
#elif !defined(CONFIG_BT_CTLR_CENTRAL_ISO)
|
||||
mfy.fp = lll_peripheral_iso_prepare;
|
||||
#else
|
||||
mfy.fp = (cig->lll.role == BT_HCI_ROLE_PERIPHERAL) ? lll_peripheral_iso_prepare :
|
||||
lll_central_iso_prepare;
|
||||
#endif
|
||||
|
||||
if (IS_PERIPHERAL(cig) && cig->sca_update) {
|
||||
/* CIG/ACL affilaition established */
|
||||
uint32_t iso_interval_us_frac =
|
||||
EVENT_US_TO_US_FRAC(cig->iso_interval * CONN_INT_UNIT_US);
|
||||
|
@ -591,7 +603,8 @@ void ull_conn_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire, uint16_t
|
|||
struct ll_conn_iso_stream *cis;
|
||||
uint32_t acl_to_cig_ref_point;
|
||||
uint32_t cis_offs_to_cig_ref;
|
||||
uint32_t iso_interval_us_frac;
|
||||
uint32_t ticks_remainder;
|
||||
uint32_t ticks_periodic;
|
||||
uint32_t ready_delay_us;
|
||||
uint32_t ticker_status;
|
||||
int32_t cig_offset_us;
|
||||
|
@ -604,6 +617,7 @@ void ull_conn_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire, uint16_t
|
|||
|
||||
cis->lll.offset = cis_offs_to_cig_ref;
|
||||
cis->lll.handle = cis_handle;
|
||||
cis->lll.active = 1U;
|
||||
|
||||
/* Check if another CIS was already started and CIG ticker is
|
||||
* running. If so, we just return with updated offset and
|
||||
|
@ -616,13 +630,6 @@ void ull_conn_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire, uint16_t
|
|||
|
||||
ticker_id = TICKER_ID_CONN_ISO_BASE + ll_conn_iso_group_handle_get(cig);
|
||||
|
||||
/* Calculate interval in fractional microseconds for highest precision when
|
||||
* accumulating the window widening window size. Ticker interval is set lopsided,
|
||||
* with natural drift towards earlier timeout.
|
||||
*/
|
||||
iso_interval_us_frac = EVENT_US_TO_US_FRAC(cig->iso_interval * CONN_INT_UNIT_US) -
|
||||
cig->lll.window_widening_periodic_us_frac;
|
||||
|
||||
/* Establish the CIG reference point by adjusting ACL-to-CIS offset
|
||||
* (cis->offset) by the difference between CIG- and CIS sync delays.
|
||||
*/
|
||||
|
@ -634,35 +641,57 @@ void ull_conn_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire, uint16_t
|
|||
ready_delay_us = lll_radio_rx_ready_delay_get(0, 0);
|
||||
#endif
|
||||
|
||||
/* Calculate initial ticker offset - we're one ACL interval early */
|
||||
/* Calculate initial ticker offset */
|
||||
cig_offset_us = acl_to_cig_ref_point;
|
||||
cig_offset_us += (acl->lll.interval * CONN_INT_UNIT_US);
|
||||
cig_offset_us -= EVENT_TICKER_RES_MARGIN_US;
|
||||
cig_offset_us -= EVENT_JITTER_US;
|
||||
cig_offset_us -= ready_delay_us;
|
||||
|
||||
/* Make sure we have time to service first subevent. TODO: Improve
|
||||
* by skipping <n> interval(s) and incrementing event_count.
|
||||
*/
|
||||
LL_ASSERT(cig_offset_us > 0);
|
||||
|
||||
/* Calculate the CIG reference point of first CIG event. This
|
||||
* calculation is inaccurate. However it is the best estimate available
|
||||
* until the first anchor point for the leading CIS is available.
|
||||
*/
|
||||
cig->cig_ref_point = HAL_TICKER_TICKS_TO_US(ticks_at_expire);
|
||||
cig->cig_ref_point += (acl->lll.interval * CONN_INT_UNIT_US);
|
||||
cig->cig_ref_point += EVENT_OVERHEAD_START_US;
|
||||
cig->cig_ref_point += acl_to_cig_ref_point;
|
||||
|
||||
if (IS_PERIPHERAL(cig)) {
|
||||
uint32_t iso_interval_us_frac;
|
||||
|
||||
/* Calculate interval in fractional microseconds for highest precision when
|
||||
* accumulating the window widening window size. Ticker interval is set lopsided,
|
||||
* with natural drift towards earlier timeout.
|
||||
*/
|
||||
iso_interval_us_frac = EVENT_US_TO_US_FRAC(cig->iso_interval * ISO_INT_UNIT_US) -
|
||||
cig->lll.window_widening_periodic_us_frac;
|
||||
ticks_periodic = EVENT_US_FRAC_TO_TICKS(iso_interval_us_frac);
|
||||
ticks_remainder = EVENT_US_FRAC_TO_REMAINDER(iso_interval_us_frac);
|
||||
|
||||
/* Adjust CIG offset and reference point ahead one interval */
|
||||
cig_offset_us -= EVENT_TICKER_RES_MARGIN_US;
|
||||
cig_offset_us -= EVENT_JITTER_US;
|
||||
cig_offset_us -= ready_delay_us;
|
||||
cig_offset_us += (acl->lll.interval * CONN_INT_UNIT_US);
|
||||
|
||||
cig->cig_ref_point += (acl->lll.interval * CONN_INT_UNIT_US);
|
||||
} else {
|
||||
uint32_t iso_interval_us;
|
||||
|
||||
iso_interval_us = cig->iso_interval * ISO_INT_UNIT_US;
|
||||
ticks_periodic = HAL_TICKER_US_TO_TICKS(iso_interval_us);
|
||||
ticks_remainder = HAL_TICKER_REMAINDER(iso_interval_us);
|
||||
}
|
||||
|
||||
/* Make sure we have time to service first subevent. TODO: Improve
|
||||
* by skipping <n> interval(s) and incrementing event_count.
|
||||
*/
|
||||
LL_ASSERT(cig_offset_us > 0);
|
||||
|
||||
/* Start CIS peripheral CIG ticker */
|
||||
ticker_status = ticker_start(TICKER_INSTANCE_ID_CTLR,
|
||||
TICKER_USER_ID_ULL_HIGH,
|
||||
ticker_id,
|
||||
ticks_at_expire,
|
||||
HAL_TICKER_US_TO_TICKS(cig_offset_us),
|
||||
EVENT_US_FRAC_TO_TICKS(iso_interval_us_frac),
|
||||
EVENT_US_FRAC_TO_REMAINDER(iso_interval_us_frac),
|
||||
ticks_periodic,
|
||||
ticks_remainder,
|
||||
TICKER_NULL_LAZY,
|
||||
0,
|
||||
ull_conn_iso_ticker_cb, cig,
|
||||
|
@ -842,7 +871,8 @@ static void cis_tx_lll_flush(void *param)
|
|||
uint32_t ret;
|
||||
|
||||
lll = param;
|
||||
lll->flushed = 1;
|
||||
lll->flushed = 1U;
|
||||
lll->active = 0U;
|
||||
|
||||
cis = ll_conn_iso_stream_get(lll->handle);
|
||||
cig = cis->group;
|
||||
|
@ -921,10 +951,13 @@ static void cig_disabled_cb(void *param)
|
|||
struct ll_conn_iso_group *cig;
|
||||
|
||||
cig = HDR_LLL2ULL(param);
|
||||
cig->cig_id = 0xFF;
|
||||
cig->started = 0;
|
||||
|
||||
ll_conn_iso_group_release(cig);
|
||||
if (IS_PERIPHERAL(cig)) {
|
||||
ll_conn_iso_group_release(cig);
|
||||
} else {
|
||||
/* CIG shall be released by ll_cig_remove */
|
||||
cig->started = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void disable(uint16_t handle)
|
||||
|
|
|
@ -4,6 +4,14 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define IS_PERIPHERAL(cig) \
|
||||
(IS_ENABLED(CONFIG_BT_CTLR_PERIPHERAL_ISO) && \
|
||||
(cig->lll.role == BT_HCI_ROLE_PERIPHERAL))
|
||||
|
||||
#define IS_CENTRAL(cig) \
|
||||
(IS_ENABLED(CONFIG_BT_CTLR_CENTRAL_ISO) && \
|
||||
(cig->lll.role == BT_HCI_ROLE_CENTRAL))
|
||||
|
||||
/* Helper functions to initialize and reset ull_conn_iso module */
|
||||
int ull_conn_iso_init(void);
|
||||
int ull_conn_iso_reset(void);
|
||||
|
|
|
@ -26,6 +26,13 @@ struct ll_conn_iso_stream {
|
|||
uint16_t teardown:1; /* 1 if CIS teardown has been initiated */
|
||||
uint16_t p_max_sdu:12; /* Maximum SDU size P_To_C */
|
||||
uint16_t c_max_sdu:12; /* Maximum SDU size C_To_P */
|
||||
union {
|
||||
struct {
|
||||
uint8_t c_rtn;
|
||||
uint8_t p_rtn;
|
||||
uint16_t instant;
|
||||
} central;
|
||||
};
|
||||
};
|
||||
|
||||
struct ll_conn_iso_group {
|
||||
|
@ -50,6 +57,15 @@ struct ll_conn_iso_group {
|
|||
uint8_t cig_id;
|
||||
uint8_t started:1; /* 1 if CIG started and ticker is running */
|
||||
uint8_t sca_update:4; /* (new SCA)+1 to trigger restart of ticker */
|
||||
uint8_t cis_count:5; /* Number of configured CISes in this CIG */
|
||||
union {
|
||||
struct {
|
||||
uint8_t sca;
|
||||
uint8_t packing;
|
||||
uint8_t framing;
|
||||
uint8_t test:1; /* HCI_LE_Set_CIG_Parameters_Test */
|
||||
} central;
|
||||
};
|
||||
};
|
||||
|
||||
struct node_rx_conn_iso_req {
|
||||
|
|
|
@ -258,6 +258,7 @@ uint8_t ull_peripheral_iso_acquire(struct ll_conn *acl,
|
|||
cis->lll.nesn = 0;
|
||||
cis->lll.cie = 0;
|
||||
cis->lll.flushed = 0;
|
||||
cis->lll.active = 0;
|
||||
cis->lll.datapath_ready_rx = 0;
|
||||
|
||||
cis->lll.rx.phy = req->c_phy;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue