bluetooth: controller: Integrate CIS Terminate with controller

Hooks the CIS terminate into the controller and fixes a few minor
things in ull_conn(_iso).c
Also handles CONFIG based comilation of CIS_TERMINATE
also fixes a minor issue in helpers_pdu

Signed-off-by: Erik Brockhoff <erbr@oticon.com>
This commit is contained in:
Erik Brockhoff 2022-07-07 09:11:22 +02:00 committed by Carles Cufí
parent 22ddc01f36
commit 6cf357bd9d
14 changed files with 246 additions and 41 deletions

View file

@ -552,43 +552,69 @@ static bool is_valid_disconnect_reason(uint8_t reason)
uint8_t ll_terminate_ind_send(uint16_t handle, uint8_t reason)
{
struct ll_conn *conn;
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) || defined(CONFIG_BT_CTLR_CENTRAL_ISO)
struct ll_conn_iso_stream *cis;
#endif
if (!IS_ACL_HANDLE(handle)) {
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
if (IS_ACL_HANDLE(handle)) {
conn = ll_connected_get(handle);
conn = ll_connected_get(handle);
if (!conn) {
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
/* Is conn still connected? */
if (!conn) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
if (conn->llcp_terminate.req != conn->llcp_terminate.ack) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
if (conn->llcp_terminate.req != conn->llcp_terminate.ack) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
if (!is_valid_disconnect_reason(reason)) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (!is_valid_disconnect_reason(reason)) {
return BT_HCI_ERR_INVALID_PARAM;
}
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
conn->llcp_terminate.reason_own = reason;
conn->llcp_terminate.req++; /* (req - ack) == 1, TERM_REQ */
conn->llcp_terminate.reason_own = reason;
conn->llcp_terminate.req++; /* (req - ack) == 1, TERM_REQ */
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
uint8_t err;
uint8_t err;
err = ull_cp_terminate(conn, reason);
if (err) {
return err;
}
err = ull_cp_terminate(conn, reason);
if (err) {
return err;
}
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn->lll.role) {
ull_periph_latency_cancel(conn, handle);
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn->lll.role) {
ull_periph_latency_cancel(conn, handle);
}
return 0;
}
return 0;
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) || defined(CONFIG_BT_CTLR_CENTRAL_ISO)
if (IS_CIS_HANDLE(handle)) {
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
cis = ll_iso_stream_connected_get(handle);
if (!cis) {
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
conn = ll_connected_get(cis->lll.acl_handle);
/* Is conn still connected? */
if (!conn) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
return ull_cp_cis_terminate(conn, cis, reason);
#else
ARG_UNUSED(cis);
/* LEGACY LLCP does not support CIS Terminate procedure */
return BT_HCI_ERR_UNKNOWN_CMD;
#endif /* !defined(CONFIG_BT_LL_SW_LLCP_LEGACY) */
}
#endif /* defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) || defined(CONFIG_BT_CTLR_CENTRAL_ISO) */
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
#if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG)
@ -1378,11 +1404,7 @@ int ull_conn_llcp(struct ll_conn *conn, uint32_t ticks_at_expire, uint16_t lazy)
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
LL_ASSERT(conn->lll.handle != LLL_HANDLE_INVALID);
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
conn->llcp.prep.ticks_at_expire = ticks_at_expire;
#else /* !CONFIG_BT_CTLR_CONN_PARAM_REQ */
ARG_UNUSED(ticks_at_expire);
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
conn->llcp.prep.lazy = lazy;
ull_cp_run(conn);

View file

@ -18,9 +18,14 @@
#include "pdu.h"
#include "lll.h"
#include "lll_conn.h"
#include "ull_conn_types.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ull_tx_queue.h"
#endif
#include "isoal.h"
#include "ull_iso_types.h"
#include "ull_conn_types.h"
#include "lll_conn_iso.h"
#include "ull_conn_iso_types.h"
#include "ull_conn_internal.h"
@ -558,6 +563,12 @@ static void cis_disabled_cb(void *param)
ll_conn_iso_stream_release(cis);
cig->lll.num_cis--;
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
/* CIS terminated, triggers completion of CIS_TERMINATE_IND procedure */
/* Only used by local procedure, ignored for remote procedure */
conn->llcp.cis.terminate_ack = 1U;
#endif /* defined(CONFIG_BT_LL_SW_LLCP_LEGACY) */
/* Check if removed CIS has an ACL disassociation callback. Invoke
* the callback to allow cleanup.
*/

View file

@ -462,9 +462,12 @@ struct llcp_struct {
} cte_rsp;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */
struct {
uint8_t terminate_ack;
} cis;
uint8_t tx_buffer_alloc;
uint8_t tx_q_pause_data_mask;
}; /* struct llcp_struct */
struct ll_conn {

View file

@ -364,9 +364,11 @@ struct proc_ctx *llcp_create_local_procedure(enum llcp_proc proc)
llcp_lp_comm_init_proc(ctx);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case PROC_CIS_TERMINATE:
llcp_lp_comm_init_proc(ctx);
break;
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */
default:
/* Unknown procedure */
LL_ASSERT(0);
@ -438,9 +440,12 @@ struct proc_ctx *llcp_create_remote_procedure(enum llcp_proc proc)
llcp_rp_comm_init_proc(ctx);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case PROC_CIS_TERMINATE:
llcp_rp_comm_init_proc(ctx);
break;
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */
default:
/* Unknown procedure */
LL_ASSERT(0);

View file

@ -104,6 +104,36 @@ enum {
static void lp_comm_ntf(struct ll_conn *conn, struct proc_ctx *ctx);
static void lp_comm_terminate_invalid_pdu(struct ll_conn *conn, struct proc_ctx *ctx);
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
/**
* @brief Stop and tear down a connected ISO stream
* This function may be called to tear down a CIS.
*
* @param cig_id ID of specific ISO group
* @param cis_id ID of connected ISO stream to stop
* @param reason Termination reason
*/
static void llcp_cis_stop_by_id(uint8_t cig_id, uint8_t cis_id, uint8_t reason)
{
struct ll_conn_iso_group *cig = ll_conn_iso_group_get_by_id(cig_id);
if (cig) {
struct ll_conn_iso_stream *cis;
uint16_t cis_handle = UINT16_MAX;
/* Look through CIS's of specified group */
cis = ll_conn_iso_stream_get_by_group(cig, &cis_handle);
while (cis && cis->cis_id != cis_id) {
/* Get next CIS */
cis = ll_conn_iso_stream_get_by_group(cig, &cis_handle);
}
if (cis && cis->lll.handle == cis_handle) {
ull_conn_iso_cis_stop(cis, NULL, reason);
}
}
}
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */
/*
* LLCP Local Procedure Common FSM
*/
@ -433,9 +463,6 @@ static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
/* No notification */
llcp_lr_complete(conn);
ctx->state = LP_COMMON_STATE_IDLE;
/* Handle CIS termination */
/* TODO: Do termination */
break;
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
@ -485,6 +512,13 @@ static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
}
}
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
static bool lp_cis_terminated(struct ll_conn *conn)
{
return conn->llcp.cis.terminate_ack;
}
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */
static void lp_comm_send_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (ctx->proc) {
@ -545,7 +579,8 @@ static void lp_comm_send_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
break;
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case PROC_CIS_TERMINATE:
if (ctx->pause || !llcp_tx_alloc_peek(conn, ctx)) {
if (!lp_cis_terminated(conn) || llcp_lr_ispaused(conn) ||
!llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = LP_COMMON_STATE_WAIT_TX;
} else {
lp_comm_tx(conn, ctx);
@ -617,6 +652,17 @@ static void lp_comm_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
{
switch (evt) {
case LP_COMMON_EVT_RUN:
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
if (ctx->proc == PROC_CIS_TERMINATE) {
/* We're getting going on a CIS Terminate */
/* So we should start by requesting Terminate for the CIS in question */
/* Clear terminate ack flag, used to signal CIS Terminated */
conn->llcp.cis.terminate_ack = 0U;
llcp_cis_stop_by_id(ctx->data.cis_term.cig_id, ctx->data.cis_term.cis_id,
ctx->data.cis_term.error_code);
}
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */
if (llcp_lr_ispaused(conn)) {
ctx->state = LP_COMMON_STATE_WAIT_TX;
} else {
@ -629,7 +675,8 @@ static void lp_comm_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
}
}
static void lp_comm_st_wait_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
static void lp_comm_st_wait_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case LP_COMMON_EVT_RUN:
@ -731,7 +778,8 @@ static void lp_comm_rx_decode(struct ll_conn *conn, struct proc_ctx *ctx, struct
}
}
static void lp_comm_st_wait_rx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
static void lp_comm_st_wait_rx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case LP_COMMON_EVT_RESPONSE:
@ -833,6 +881,16 @@ static void rp_comm_terminate(struct ll_conn *conn, struct proc_ctx *ctx)
/* Mark the connection for termination */
conn->llcp_terminate.reason_final = ctx->data.term.error_code;
}
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
static void rp_comm_stop_cis(struct proc_ctx *ctx)
{
llcp_cis_stop_by_id(ctx->data.cis_term.cig_id, ctx->data.cis_term.cis_id,
ctx->data.cis_term.error_code);
}
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */
/*
* LLCP Remote Procedure Common FSM
*/
@ -874,9 +932,13 @@ static void rp_comm_rx_decode(struct ll_conn *conn, struct proc_ctx *ctx, struct
/* Make sure no data is tx'ed after RX of terminate ind */
llcp_tx_pause_data(conn, LLCP_TX_QUEUE_PAUSE_DATA_TERMINATE);
break;
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case PDU_DATA_LLCTRL_TYPE_CIS_TERMINATE_IND:
llcp_pdu_decode_cis_terminate_ind(ctx, pdu);
/* Terminate CIS */
rp_comm_stop_cis(ctx);
break;
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ:
llcp_pdu_decode_length_req(conn, pdu);

View file

@ -196,9 +196,11 @@ void llcp_lr_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *
llcp_lp_comm_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case PROC_CIS_TERMINATE:
llcp_lp_comm_rx(conn, ctx, rx);
break;
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */
default:
/* Unknown procedure */
LL_ASSERT(0);
@ -229,9 +231,11 @@ void llcp_lr_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *
llcp_lp_pu_tx_ack(conn, ctx, tx);
break;
#endif /* CONFIG_BT_CTLR_PHY */
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case PROC_CIS_TERMINATE:
llcp_lp_comm_tx_ack(conn, ctx, tx);
break;
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */
default:
break;
/* Ignore tx_ack */
@ -296,9 +300,11 @@ static void lr_act_run(struct ll_conn *conn)
llcp_lp_comm_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case PROC_CIS_TERMINATE:
llcp_lp_comm_run(conn, ctx, NULL);
break;
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */
default:
/* Unknown procedure */
LL_ASSERT(0);

View file

@ -835,6 +835,7 @@ void llcp_pdu_encode_cte_rsp(const struct proc_ctx *ctx, struct pdu_data *pdu)
}
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
void llcp_pdu_encode_cis_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_cis_terminate_ind *p;
@ -857,3 +858,4 @@ void llcp_pdu_decode_cis_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pd
ctx->data.cis_term.cis_id = pdu->llctrl.cis_terminate_ind.cis_id;
ctx->data.cis_term.error_code = pdu->llctrl.cis_terminate_ind.error_code;
}
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */

View file

@ -44,6 +44,7 @@ FILE(GLOB mock_sources
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/util.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ticker.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull_conn_iso.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull_peripheral.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull_central.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull_scan.c

View file

@ -183,7 +183,7 @@
#define FEAT_ISO_CENTRAL 0x00
#endif
#if defined(CONFIG_MISSING)
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
#define FEAT_ISO_PERIPHERAL 0x20000000
#else
#define FEAT_ISO_PERIPHERAL 0x00

View file

@ -116,7 +116,18 @@ void test_cis_terminate_loc(uint8_t role)
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* 'Signal' CIS terminated */
conn.llcp.cis.terminate_ack = 1;
/* Prepare */
event_prepare(&conn);
/* Tx Queue should now have one LL Control PDU */
lt_rx(LL_CIS_TERMINATE_IND, &conn, &tx, &local_cis_terminate_ind);
lt_rx_q_is_empty(&conn);

View file

@ -353,7 +353,7 @@ void test_hci_terminate(void)
reason = 0x01;
err = ll_terminate_ind_send(conn_handle + 1, reason);
zassert_equal(err, BT_HCI_ERR_UNKNOWN_CONN_ID, "Errorcode %d", err);
zassert_equal(err, BT_HCI_ERR_CMD_DISALLOWED, "Errorcode %d", err);
err = ll_terminate_ind_send(conn_handle, reason);
zassert_equal(err, BT_HCI_ERR_INVALID_PARAM, "Errorcode %d", err);
reason = BT_HCI_ERR_REMOTE_USER_TERM_CONN;

View file

@ -166,6 +166,10 @@
#define CONFIG_BT_CTLR_DF_MAX_ANT_SW_PATTERN_LEN 38
#endif
#ifndef CONFIG_BT_CTLR_PERIPHERAL_ISO
#define CONFIG_BT_CTLR_PERIPHERAL_ISO y
#endif
/* Kconfig Cheats */
#define CONFIG_BT_LOG_LEVEL 1
#define CONFIG_BT_CTLR_COMPANY_ID 0x1234
@ -174,3 +178,4 @@
#define CONFIG_BT_BUF_ACL_TX_COUNT 7
#define CONFIG_BT_BUF_ACL_TX_SIZE 27
#define CONFIG_BT_CTLR_RX_BUFFERS 7
#define CONFIG_NET_BUF_USER_DATA_SIZE 8

View file

@ -37,8 +37,6 @@
#include "ull_iso_types.h"
#include "ull_conn_iso_types.h"
#include "ull_llcp.h"
extern sys_slist_t ut_rx_q;
__attribute__((weak)) int lll_csrand_get(void *buf, size_t len)

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2021 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include "hal/ccm.h"
#include "hal/ticker.h"
#include "hal/cpu.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "util/dbuf.h"
#include "util/mayfly.h"
#include "util/mfifo.h"
#include "ticker/ticker.h"
#include "pdu.h"
#include "lll.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ull_tx_queue.h"
#endif
#include "isoal.h"
#include "ull_iso_types.h"
#include "ull_conn_types.h"
#include "lll_conn_iso.h"
#include "ull_conn_iso_types.h"
#include "ull_conn_internal.h"
#include "ull_conn_iso_internal.h"
#include "ull_internal.h"
#include "lll/lll_vendor.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_group cig = { 0 };
static struct ll_conn_iso_stream cis = { .established = 1};
struct ll_conn_iso_stream *ll_conn_iso_stream_get_by_acl(struct ll_conn *conn, uint16_t *cis_iter)
{
return &cis;
}
struct ll_conn_iso_stream *ll_conn_iso_stream_get(uint16_t handle)
{
return &cis;
}
struct ll_conn_iso_group *ll_conn_iso_group_get_by_id(uint8_t id)
{
return &cig;
}
struct ll_conn_iso_stream *ll_conn_iso_stream_get_by_group(struct ll_conn_iso_group *cig,
uint16_t *handle_iter)
{
return NULL;
}
void ull_conn_iso_cis_stop(struct ll_conn_iso_stream *cis,
ll_iso_stream_released_cb_t cis_released_cb,
uint8_t reason)
{
}
void ull_conn_iso_cis_stop_by_id(uint8_t cig_id, uint8_t cis_id, uint8_t reason)
{
}