diff --git a/subsys/bluetooth/controller/CMakeLists.txt b/subsys/bluetooth/controller/CMakeLists.txt index 4b86103fb3b..82a15ef9d9a 100644 --- a/subsys/bluetooth/controller/CMakeLists.txt +++ b/subsys/bluetooth/controller/CMakeLists.txt @@ -78,6 +78,12 @@ if(CONFIG_BT_LL_SW_SPLIT) CONFIG_BT_CTLR_LE_ENC ll_sw/ull_llcp_enc.c ) + if (CONFIG_BT_CTLR_PERIPHERAL_ISO OR + CONFIG_BT_CTLR_CENTRAL_ISO) + zephyr_library_sources( + ll_sw/ull_llcp_cc.c + ) + endif() zephyr_library_sources( ll_sw/ull_tx_queue.c ll_sw/ull_llcp.c diff --git a/subsys/bluetooth/controller/ll_sw/lll.h b/subsys/bluetooth/controller/ll_sw/lll.h index 835d1056b84..5e57c99e198 100644 --- a/subsys/bluetooth/controller/ll_sw/lll.h +++ b/subsys/bluetooth/controller/ll_sw/lll.h @@ -27,6 +27,10 @@ #define ISO_INT_UNIT_US CONN_INT_UNIT_US #define PERIODIC_INT_UNIT_US 1250U +/* Timeout for Host to accept/reject cis create request */ +/* See BTCore5.3, 4.E.6.7 - Default value 0x1f40 * 625us */ +#define DEFAULT_CONNECTION_ACCEPT_TIMEOUT_US (5 * USEC_PER_SEC) + /* Intervals after which connection or sync establishment is considered lost */ #define CONN_ESTAB_COUNTDOWN 6U diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c b/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c index 10c44395610..751f411f13c 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c @@ -225,6 +225,7 @@ struct ll_conn_iso_stream *ll_conn_iso_stream_get_by_group(struct ll_conn_iso_gr void ull_conn_iso_cis_established(struct ll_conn_iso_stream *cis) { +#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY) struct node_rx_conn_iso_estab *est; struct node_rx_pdu *node_rx; @@ -245,6 +246,7 @@ void ull_conn_iso_cis_established(struct ll_conn_iso_stream *cis) ll_rx_put(node_rx->hdr.link, node_rx); ll_rx_sched(); +#endif /* defined(CONFIG_BT_LL_SW_LLCP_LEGACY) */ cis->established = 1; } diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h index 16bc4b0cd50..28dab3df69a 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h @@ -541,7 +541,7 @@ struct ll_conn { uint16_t connect_expire; uint16_t supervision_reload; uint16_t supervision_expire; - + uint32_t connect_accept_to; #if defined(CONFIG_BT_CTLR_PHY) uint8_t phy_pref_tx:3; diff --git a/subsys/bluetooth/controller/ll_sw/ull_iso.c b/subsys/bluetooth/controller/ll_sw/ull_iso.c index 4ed3c4d2e55..ea2c1980007 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_iso.c @@ -30,6 +30,10 @@ #include "lll_conn_iso.h" #include "lll_iso_tx.h" +#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY) +#include "ull_tx_queue.h" +#endif + #include "isoal.h" #include "ull_adv_types.h" @@ -44,6 +48,7 @@ #include "ull_sync_iso_internal.h" #include "ull_conn_iso_internal.h" #include "ull_conn_types.h" +#include "ull_llcp.h" #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) #define LOG_MODULE_NAME bt_ctlr_ull_iso @@ -248,9 +253,12 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id, * disallowed status. */ #if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) +#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY) const uint8_t cis_waiting = (conn->llcp_cis.state == LLCP_CIS_STATE_RSP_WAIT); - +#else + const uint8_t cis_waiting = ull_cp_cc_awaiting_reply(conn); +#endif if (cis_waiting) { return BT_HCI_ERR_CMD_DISALLOWED; } diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.c b/subsys/bluetooth/controller/ll_sw/ull_llcp.c index 6429b9545ed..4127100039e 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.c @@ -440,6 +440,11 @@ 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_PERIPHERAL) && defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) + case PROC_CIS_CREATE: + llcp_rp_cc_init_proc(ctx); + break; +#endif /* CONFIG_BT_PERIPHERAL && CONFIG_BT_CTLR_PERIPHERAL_ISO */ #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) case PROC_CIS_TERMINATE: llcp_rp_comm_init_proc(ctx); @@ -1089,6 +1094,53 @@ void ull_cp_cte_req_set_disable(struct ll_conn *conn) conn->llcp.cte_req.req_interval = 0U; } #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ +#if defined(CONFIG_BT_PERIPHERAL) && defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) +bool ull_cp_cc_awaiting_reply(struct ll_conn *conn) +{ + struct proc_ctx *ctx; + + ctx = llcp_rr_peek(conn); + if (ctx && ctx->proc == PROC_CIS_CREATE) { + return llcp_rp_cc_awaiting_reply(ctx); + } + + return false; + +} + +uint16_t ull_cp_cc_ongoing_handle(struct ll_conn *conn) +{ + struct proc_ctx *ctx; + + ctx = llcp_rr_peek(conn); + if (ctx && ctx->proc == PROC_CIS_CREATE) { + return ctx->data.cis_create.cis_handle; + } + + return 0xFFFF; +} + +void ull_cp_cc_accept(struct ll_conn *conn) +{ + struct proc_ctx *ctx; + + ctx = llcp_rr_peek(conn); + if (ctx && ctx->proc == PROC_CIS_CREATE) { + llcp_rp_cc_accept(conn, ctx); + } +} + +void ull_cp_cc_reject(struct ll_conn *conn, uint8_t error_code) +{ + struct proc_ctx *ctx; + + ctx = llcp_rr_peek(conn); + if (ctx && ctx->proc == PROC_CIS_CREATE) { + ctx->data.cis_create.error = error_code; + llcp_rp_cc_reject(conn, ctx); + } +} +#endif /* defined(CONFIG_BT_PERIPHERAL) && defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ static bool pdu_is_expected(struct pdu_data *pdu, struct proc_ctx *ctx) { diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.h b/subsys/bluetooth/controller/ll_sw/ull_llcp.h index 69233575152..d443daac692 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.h @@ -158,6 +158,27 @@ uint8_t ull_cp_terminate(struct ll_conn *conn, uint8_t error_code); uint8_t ull_cp_cis_terminate(struct ll_conn *conn, struct ll_conn_iso_stream *cis, uint8_t error_code); +/** + * @brief Is ongoing create cis procedure expecting a reply? + */ +bool ull_cp_cc_awaiting_reply(struct ll_conn *conn); + +/** + * @brief Get handle of ongoing create cis procedure. + * @return 0xFFFF if none + */ +uint16_t ull_cp_cc_ongoing_handle(struct ll_conn *conn); + +/** + * @brief Accept the remote device’s request to create cis. + */ +void ull_cp_cc_accept(struct ll_conn *conn); + +/** + * @brief Rejset the remote device’s request to create cis. + */ +void ull_cp_cc_reject(struct ll_conn *conn, uint8_t error_code); + /** * @brief Initiate a Channel Map Update Procedure. */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c new file mode 100644 index 00000000000..06f4899df0f --- /dev/null +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2022 Demant + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include + +#include "hal/ecb.h" +#include "hal/ccm.h" + +#include "util/util.h" +#include "util/mem.h" +#include "util/memq.h" +#include "util/dbuf.h" + +#include "pdu.h" +#include "ll.h" +#include "ll_feat.h" +#include "ll_settings.h" + +#include "lll.h" +#include "lll/lll_df_types.h" +#include "lll_conn.h" +#include "lll_conn_iso.h" + +#include "ull_tx_queue.h" + +#include "isoal.h" +#include "ull_iso_types.h" +#include "ull_conn_types.h" +#include "ull_conn_iso_types.h" +#include "ull_internal.h" +#include "ull_llcp.h" +#include "ull_llcp_internal.h" +#include "ull_llcp_features.h" +#include "ull_conn_internal.h" + +#include "ull_iso_internal.h" +#include "ull_conn_iso_internal.h" +#include "ull_peripheral_iso_internal.h" + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) +#define LOG_MODULE_NAME bt_ctlr_ull_llcp_cis +#include "common/log.h" +#include +#include "hal/debug.h" + +static uint16_t cc_event_counter(struct ll_conn *conn) +{ + struct lll_conn *lll; + uint16_t event_counter; + + uint16_t lazy = conn->llcp.prep.lazy; + + /**/ + lll = &conn->lll; + + /* Calculate current event counter */ + event_counter = lll->event_counter + lll->latency_prepare + lazy; + + return event_counter; +} + +#if defined(CONFIG_BT_PERIPHERAL) +/* LLCP Remote Procedure FSM states */ +enum { + /* Establish Procedure */ + RP_CC_STATE_IDLE, + RP_CC_STATE_WAIT_RX_CIS_REQ, + RP_CC_STATE_WAIT_NTF_CIS_CREATE, + RP_CC_STATE_WAIT_REPLY, + RP_CC_STATE_WAIT_TX_CIS_RSP, + RP_CC_STATE_WAIT_TX_REJECT_IND, + RP_CC_STATE_WAIT_RX_CIS_IND, + RP_CC_STATE_WAIT_INSTANT, + RP_CC_STATE_WAIT_CIS_ESTABLISHED, + RP_CC_STATE_WAIT_NTF, +}; + +/* LLCP Remote Procedure FSM events */ +enum { + /* Procedure prepared */ + RP_CC_EVT_RUN, + + /* Request received */ + RP_CC_EVT_CIS_REQ, + + /* Response received */ + RP_CC_EVT_CIS_RSP, + + /* Indication received */ + RP_CC_EVT_CIS_IND, + + /* Create request accept reply */ + RP_CC_EVT_CIS_REQ_ACCEPT, + + /* Create request decline reply */ + RP_CC_EVT_CIS_REQ_REJECT, + + /* Reject response received */ + RP_CC_EVT_REJECT, + + /* Unknown response received */ + RP_CC_EVT_UNKNOWN, +}; +/* + * LLCP Remote Procedure FSM + */ + +static void llcp_rp_cc_tx_rsp(struct ll_conn *conn, struct proc_ctx *ctx) +{ + struct node_tx *tx; + struct pdu_data *pdu; + + /* Allocate tx node */ + tx = llcp_tx_alloc(conn, ctx); + LL_ASSERT(tx); + + pdu = (struct pdu_data *)tx->pdu; + + llcp_pdu_encode_cis_rsp(ctx, pdu); + ctx->tx_opcode = pdu->llctrl.opcode; + + /* Enqueue LL Control PDU towards LLL */ + llcp_tx_enqueue(conn, tx); +} + +static void llcp_rp_cc_tx_reject(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode) +{ + struct node_tx *tx; + struct pdu_data *pdu; + + /* Allocate tx node */ + tx = llcp_tx_alloc(conn, ctx); + LL_ASSERT(tx); + + pdu = (struct pdu_data *)tx->pdu; + + /* Encode LL Control PDU */ + llcp_pdu_encode_reject_ext_ind(pdu, opcode, ctx->data.cis_create.error); + ctx->tx_opcode = pdu->llctrl.opcode; + + /* Enqueue LL Control PDU towards LLL */ + llcp_tx_enqueue(conn, tx); +} + +static void rp_cc_ntf_create(struct ll_conn *conn, struct proc_ctx *ctx) +{ + struct node_rx_pdu *ntf; + struct node_rx_conn_iso_req *pdu; + + /* Allocate ntf node */ + ntf = llcp_ntf_alloc(); + LL_ASSERT(ntf); + + ntf->hdr.type = NODE_RX_TYPE_CIS_REQUEST; + ntf->hdr.handle = conn->lll.handle; + pdu = (struct node_rx_conn_iso_req *)ntf->pdu; + + pdu->cig_id = ctx->data.cis_create.cig_id; + pdu->cis_id = ctx->data.cis_create.cis_id; + pdu->cis_handle = ctx->data.cis_create.cis_handle; + + ctx->data.cis_create.host_request_to = 0U; + + /* Enqueue notification towards LL */ + ll_rx_put(ntf->hdr.link, ntf); + ll_rx_sched(); +} + + +static void rp_cc_ntf(struct ll_conn *conn, struct proc_ctx *ctx) +{ + struct node_rx_pdu *ntf; + struct node_rx_conn_iso_estab *pdu; + + /* Allocate ntf node */ + ntf = llcp_ntf_alloc(); + LL_ASSERT(ntf); + + ntf->hdr.type = NODE_RX_TYPE_CIS_ESTABLISHED; + ntf->hdr.handle = conn->lll.handle; + ntf->hdr.rx_ftr.param = ll_conn_iso_stream_get(ctx->data.cis_create.cis_handle); + + pdu = (struct node_rx_conn_iso_estab *)ntf->pdu; + + pdu->cis_handle = ctx->data.cis_create.cis_handle; + pdu->status = ctx->data.cis_create.error; + + /* Enqueue notification towards LL */ + ll_rx_put(ntf->hdr.link, ntf); + ll_rx_sched(); +} + +static void rp_cc_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) +{ + if (!llcp_ntf_alloc_is_available()) { + ctx->state = RP_CC_STATE_WAIT_NTF; + } else { + rp_cc_ntf(conn, ctx); + llcp_rr_complete(conn); + ctx->state = RP_CC_STATE_IDLE; + } +} + +static void rp_cc_send_cis_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + if (llcp_rr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { + ctx->state = RP_CC_STATE_WAIT_TX_CIS_RSP; + } else { + llcp_rp_cc_tx_rsp(conn, ctx); + + /* Wait for the LL_CIS_IND */ + ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CIS_IND; + ctx->state = RP_CC_STATE_WAIT_RX_CIS_IND; + } +} + +static void rp_cc_send_create_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + if (!llcp_ntf_alloc_is_available()) { + ctx->state = RP_CC_STATE_WAIT_NTF_CIS_CREATE; + } else { + rp_cc_ntf_create(conn, ctx); + ctx->state = RP_CC_STATE_WAIT_REPLY; + } +} + +static void rp_cc_send_reject_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + if (llcp_rr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { + ctx->state = RP_CC_STATE_WAIT_TX_REJECT_IND; + } else { + llcp_rp_cc_tx_reject(conn, ctx, PDU_DATA_LLCTRL_TYPE_CIS_REQ); + + if (ctx->data.cis_create.error == BT_HCI_ERR_CONN_ACCEPT_TIMEOUT) { + /* We reject due to an accept timeout, so we should generate NTF */ + rp_cc_complete(conn, ctx, evt, param); + } else { + /* Otherwise we quietly complete the procedure */ + llcp_rr_complete(conn); + ctx->state = RP_CC_STATE_IDLE; + } + } +} + +static void rp_cc_state_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case RP_CC_EVT_RUN: + ctx->state = RP_CC_STATE_WAIT_RX_CIS_REQ; + break; + default: + /* Ignore other evts */ + break; + } +} + +static inline bool phy_valid(uint8_t phy) +{ + /* This is equivalent to: + * exactly one bit set, and no bit set is rfu's + */ + return (phy == PHY_1M || phy == PHY_2M || phy == PHY_CODED); +} + +static uint8_t rp_cc_check_phy(struct ll_conn *conn, struct proc_ctx *ctx, + struct pdu_data *pdu) +{ + if (!phy_valid(pdu->llctrl.cis_req.c_phy) || + !phy_valid(pdu->llctrl.cis_req.p_phy)) { + /* zero, more than one or any rfu bit selected in either phy */ + return BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL; + } + +#if defined(CONFIG_BT_CTLR_PHY) + const uint8_t phys = pdu->llctrl.cis_req.p_phy | pdu->llctrl.cis_req.c_phy; + + if (((phys & PHY_2M) && !feature_phy_2m(conn)) || + ((phys & PHY_CODED) && !feature_phy_coded(conn))) { + /* Unsupported phy selected */ + return BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL; + } +#endif + + return BT_HCI_ERR_SUCCESS; +} + +static bool rp_cc_check_cis_established_lll(struct proc_ctx *ctx) +{ + const struct ll_conn_iso_stream *cis = + ll_conn_iso_stream_get(ctx->data.cis_create.cis_handle); + + return cis->established; +} + +static void rp_cc_state_wait_rx_cis_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + struct pdu_data *pdu = (struct pdu_data *)param; + + switch (evt) { + case RP_CC_EVT_CIS_REQ: + /* Handle CIS request */ + llcp_pdu_decode_cis_req(ctx, pdu); + + /* Check PHY */ + ctx->data.cis_create.error = rp_cc_check_phy(conn, ctx, pdu); + + if (ctx->data.cis_create.error == BT_HCI_ERR_SUCCESS) { + ctx->data.cis_create.error = + ull_peripheral_iso_acquire(conn, &pdu->llctrl.cis_req, + &ctx->data.cis_create.cis_handle); + } + + if (ctx->data.cis_create.error == BT_HCI_ERR_SUCCESS) { + /* Now controller accepts, so go ask the host to accept or decline */ + rp_cc_send_create_ntf(conn, ctx, evt, param); + } else { + /* Now controller rejects, right out */ + rp_cc_send_reject_ind(conn, ctx, evt, param); + } + break; + default: + /* Ignore other evts */ + break; + } +} + +static void rp_cc_state_wait_tx_cis_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case RP_CC_EVT_RUN: + rp_cc_send_cis_rsp(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void rp_cc_state_wait_tx_reject_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case RP_CC_EVT_RUN: + rp_cc_send_reject_ind(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void rp_cc_state_wait_rx_cis_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + struct pdu_data *pdu = (struct pdu_data *)param; + + switch (evt) { + case RP_CC_EVT_CIS_IND: + if (!ull_peripheral_iso_setup(&pdu->llctrl.cis_ind, ctx->data.cis_create.cig_id, + ctx->data.cis_create.cis_handle)) { + + /* CIS has been setup, go wait for 'instant' before starting */ + ctx->state = RP_CC_STATE_WAIT_INSTANT; + + /* Fixme - Implement CIS Supervision timeout + * Spec: + * When establishing a CIS, the Peripheral shall start the CIS supervision + * timer at the start of the next CIS event after receiving the LL_CIS_IND. + * If the CIS supervision timer reaches 6 * ISO_Interval before the CIS is + * established, the CIS shall be considered lost. + */ + + break; + } + /* If we get to here the CIG_ID referred in req/acquire has become void/invalid */ + /* This cannot happen unless the universe has started to deflate */ + LL_ASSERT(0); + case RP_CC_EVT_REJECT: + /* Handle CIS creation rejection */ + break; + + default: + /* Ignore other evts */ + break; + } +} + +static void rp_cc_state_wait_ntf_cis_create(struct ll_conn *conn, struct proc_ctx *ctx, + uint8_t evt, void *param) +{ + switch (evt) { + case RP_CC_EVT_RUN: + rp_cc_send_create_ntf(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + + +static void rp_cc_state_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case RP_CC_EVT_RUN: + rp_cc_complete(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void rp_cc_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + uint16_t start_event_count; + + start_event_count = ctx->data.cis_create.conn_event_count; + + if (is_instant_reached_or_passed(start_event_count, + cc_event_counter(conn))) { + /* Start CIS */ + ull_peripheral_iso_start(conn, conn->llcp.prep.ticks_at_expire, + ctx->data.cis_create.cis_handle); + + /* Now we can wait for CIS to become established */ + ctx->state = RP_CC_STATE_WAIT_CIS_ESTABLISHED; + } +} + +static void rp_cc_state_wait_reply(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + + switch (evt) { + case RP_CC_EVT_CIS_REQ_ACCEPT: + /* Continue procedure in next prepare run */ + ctx->state = RP_CC_STATE_WAIT_TX_CIS_RSP; + break; + case RP_CC_EVT_RUN: + /* Update 'time' and check for timeout on the Reply */ + ctx->data.cis_create.host_request_to += (conn->lll.interval * CONN_INT_UNIT_US); + if (ctx->data.cis_create.host_request_to < conn->connect_accept_to) { + break; + } + /* Reject 'reason/error' */ + ctx->data.cis_create.error = BT_HCI_ERR_CONN_ACCEPT_TIMEOUT; + /* If timeout is hit, fall through and reject */ + case RP_CC_EVT_CIS_REQ_REJECT: + /* Continue procedure in next prepare run */ + ctx->state = RP_CC_STATE_WAIT_TX_REJECT_IND; + break; + default: + /* Ignore other evts */ + break; + } +} + + +static void rp_cc_state_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case RP_CC_EVT_RUN: + rp_cc_check_instant(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + + +static void rp_cc_state_wait_cis_established(struct ll_conn *conn, struct proc_ctx *ctx, + uint8_t evt, void *param) +{ + switch (evt) { + case RP_CC_EVT_RUN: + /* Check for CIS state */ + if (rp_cc_check_cis_established_lll(ctx)) { + /* CIS was established, so let's got ahead and complete procedure */ + rp_cc_complete(conn, ctx, evt, param); + } + break; + default: + /* Ignore other evts */ + break; + } +} + + +static void rp_cc_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) +{ + switch (ctx->state) { + /* Create Procedure */ + case RP_CC_STATE_IDLE: + rp_cc_state_idle(conn, ctx, evt, param); + break; + case RP_CC_STATE_WAIT_RX_CIS_REQ: + rp_cc_state_wait_rx_cis_req(conn, ctx, evt, param); + break; + case RP_CC_STATE_WAIT_NTF_CIS_CREATE: + rp_cc_state_wait_ntf_cis_create(conn, ctx, evt, param); + break; + case RP_CC_STATE_WAIT_TX_REJECT_IND: + rp_cc_state_wait_tx_reject_ind(conn, ctx, evt, param); + break; + case RP_CC_STATE_WAIT_TX_CIS_RSP: + rp_cc_state_wait_tx_cis_rsp(conn, ctx, evt, param); + break; + case RP_CC_STATE_WAIT_REPLY: + rp_cc_state_wait_reply(conn, ctx, evt, param); + break; + case RP_CC_STATE_WAIT_RX_CIS_IND: + rp_cc_state_wait_rx_cis_ind(conn, ctx, evt, param); + break; + case RP_CC_STATE_WAIT_INSTANT: + rp_cc_state_wait_instant(conn, ctx, evt, param); + break; + case RP_CC_STATE_WAIT_CIS_ESTABLISHED: + rp_cc_state_wait_cis_established(conn, ctx, evt, param); + break; + case RP_CC_STATE_WAIT_NTF: + rp_cc_state_wait_ntf(conn, ctx, evt, param); + break; + default: + /* Unknown state */ + LL_ASSERT(0); + } +} + +void llcp_rp_cc_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx) +{ + struct pdu_data *pdu = (struct pdu_data *)rx->pdu; + + switch (pdu->llctrl.opcode) { + case PDU_DATA_LLCTRL_TYPE_CIS_REQ: + rp_cc_execute_fsm(conn, ctx, RP_CC_EVT_CIS_REQ, pdu); + break; + case PDU_DATA_LLCTRL_TYPE_CIS_IND: + rp_cc_execute_fsm(conn, ctx, RP_CC_EVT_CIS_IND, pdu); + break; + case PDU_DATA_LLCTRL_TYPE_REJECT_IND: + case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND: + rp_cc_execute_fsm(conn, ctx, RP_CC_EVT_REJECT, pdu); + break; + default: + /* Unknown opcode */ + LL_ASSERT(0); + } +} + +void llcp_rp_cc_init_proc(struct proc_ctx *ctx) +{ + switch (ctx->proc) { + case PROC_CIS_CREATE: + ctx->state = RP_CC_STATE_IDLE; + break; + default: + LL_ASSERT(0); + } +} + +bool llcp_rp_cc_awaiting_reply(struct proc_ctx *ctx) +{ + return (ctx->state == RP_CC_STATE_WAIT_REPLY); +} + +void llcp_rp_cc_accept(struct ll_conn *conn, struct proc_ctx *ctx) +{ + rp_cc_execute_fsm(conn, ctx, RP_CC_EVT_CIS_REQ_ACCEPT, NULL); +} + +void llcp_rp_cc_reject(struct ll_conn *conn, struct proc_ctx *ctx) +{ + rp_cc_execute_fsm(conn, ctx, RP_CC_EVT_CIS_REQ_REJECT, NULL); +} + +void llcp_rp_cc_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param) +{ + rp_cc_execute_fsm(conn, ctx, RP_CC_EVT_RUN, param); +} +#endif /* CONFIG_BT_PERIPHERAL */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c index 1aced8d5fb5..ba399d74273 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c @@ -885,7 +885,6 @@ static void rp_comm_terminate(struct ll_conn *conn, struct proc_ctx *ctx) #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); } diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h index 69ca8485122..cf9daf41063 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h @@ -26,6 +26,7 @@ enum llcp_proc { PROC_CHAN_MAP_UPDATE, PROC_DATA_LENGTH_UPDATE, PROC_CTE_REQ, + PROC_CIS_CREATE, PROC_CIS_TERMINATE, /* A helper enum entry, to use in pause procedure context */ PROC_NONE = 0x0, @@ -249,6 +250,40 @@ struct proc_ctx { uint8_t min_cte_len; } cte_remote_req; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ + struct { + uint8_t error; + uint16_t cis_handle; + uint8_t cig_id; + uint8_t cis_id; + uint16_t conn_event_count; + uint32_t cis_offset_min; + uint32_t cis_offset_max; +#if defined(CONFIG_BT_PERIPHERAL) + uint32_t host_request_to; +#endif /* defined(CONFIG_BT_PERIPHERAL) */ +#if defined(CONFIG_BT_CENTRAL) + /* In case of a cis_req data decoded by ull_peripheral_iso_acquire, so + * only need to store info in local create (ie central enabled device) + */ + uint8_t c_phy; + uint8_t p_phy; + uint16_t c_max_sdu; + uint16_t p_max_sdu; + uint8_t framed; + uint32_t c_sdu_interval; + uint32_t p_sdu_interval; + uint8_t nse; + uint16_t c_max_pdu; + uint16_t p_max_pdu; + uint32_t sub_interval; + uint8_t p_bn; + uint8_t c_bn; + uint8_t c_ft; + uint8_t p_ft; + uint16_t iso_interval; +#endif /* defined(CONFIG_BT_CENTRAL) */ + } cis_create; + struct { uint8_t cig_id; uint8_t cis_id; @@ -642,6 +677,19 @@ void llcp_pdu_decode_cte_req(struct proc_ctx *ctx, struct pdu_data *pdu); void llcp_pdu_encode_cte_rsp(const struct proc_ctx *ctx, struct pdu_data *pdu); #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +void llcp_lp_cc_init_proc(struct proc_ctx *ctx); +void llcp_lp_cc_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx); +void llcp_lp_cc_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param); + +void llcp_rp_cc_init_proc(struct proc_ctx *ctx); +void llcp_rp_cc_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx); +void llcp_rp_cc_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param); +bool llcp_rp_cc_awaiting_reply(struct proc_ctx *ctx); +void llcp_rp_cc_accept(struct ll_conn *conn, struct proc_ctx *ctx); +void llcp_rp_cc_reject(struct ll_conn *conn, struct proc_ctx *ctx); + +void llcp_pdu_decode_cis_req(struct proc_ctx *ctx, struct pdu_data *pdu); +void llcp_pdu_encode_cis_rsp(struct proc_ctx *ctx, struct pdu_data *pdu); void llcp_pdu_encode_cis_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu); void llcp_pdu_decode_cis_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c index cd7cb312599..580267768a1 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c @@ -835,6 +835,51 @@ 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_PERIPHERAL_ISO) +void llcp_pdu_decode_cis_req(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + ctx->data.cis_create.cig_id = pdu->llctrl.cis_req.cig_id; + ctx->data.cis_create.cis_id = pdu->llctrl.cis_req.cis_id; + ctx->data.cis_create.cis_offset_min = sys_get_le24(pdu->llctrl.cis_req.cis_offset_min); + ctx->data.cis_create.cis_offset_max = sys_get_le24(pdu->llctrl.cis_req.cis_offset_max); + ctx->data.cis_create.conn_event_count = + sys_le16_to_cpu(pdu->llctrl.cis_req.conn_event_count); + /* The remainder of the req is decoded by ull_peripheral_iso_acquire, so + * no need to do it here too + ctx->data.cis_create.c_phy = pdu->llctrl.cis_req.c_phy; + ctx->data.cis_create.p_phy = pdu->llctrl.cis_req.p_phy; + ctx->data.cis_create.c_max_sdu = pdu->llctrl.cis_req.c_max_sdu; + ctx->data.cis_create.p_max_sdu = pdu->llctrl.cis_req.p_max_sdu; + ctx->data.cis_create.framed = pdu->llctrl.cis_req.framed; + ctx->data.cis_create.c_sdu_interval = sys_get_le24(pdu->llctrl.cis_req.c_sdu_interval); + ctx->data.cis_create.p_sdu_interval = sys_get_le24(pdu->llctrl.cis_req.p_sdu_interval); + ctx->data.cis_create.nse = pdu->llctrl.cis_req.nse; + ctx->data.cis_create.c_max_pdu = sys_le16_to_cpu(pdu->llctrl.cis_req.c_max_pdu); + ctx->data.cis_create.p_max_pdu = sys_le16_to_cpu(pdu->llctrl.cis_req.p_max_pdu); + ctx->data.cis_create.sub_interval = sys_get_le24(pdu->llctrl.cis_req.sub_interval); + ctx->data.cis_create.p_bn = pdu->llctrl.cis_req.p_bn; + ctx->data.cis_create.c_bn = pdu->llctrl.cis_req.c_bn; + ctx->data.cis_create.c_ft = pdu->llctrl.cis_req.c_ft; + ctx->data.cis_create.p_ft = pdu->llctrl.cis_req.p_ft; + ctx->data.cis_create.iso_interval = sys_le16_to_cpu(pdu->llctrl.cis_req.iso_interval); + */ +} + +void llcp_pdu_encode_cis_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + struct pdu_data_llctrl_cis_rsp *p; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = + offsetof(struct pdu_data_llctrl, cis_rsp) + sizeof(struct pdu_data_llctrl_cis_rsp); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CIS_RSP; + + p = &pdu->llctrl.cis_rsp; + sys_put_le24(ctx->data.cis_create.cis_offset_max, p->cis_offset_max); + sys_put_le24(ctx->data.cis_create.cis_offset_min, p->cis_offset_min); + p->conn_event_count = sys_cpu_to_le16(ctx->data.cis_create.conn_event_count); +} +#endif /* defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ #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) { diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c index 891ae1e0471..d914acfae86 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c @@ -93,6 +93,7 @@ static bool proc_with_instant(struct proc_ctx *ctx) case PROC_DATA_LENGTH_UPDATE: case PROC_CTE_REQ: case PROC_CIS_TERMINATE: + case PROC_CIS_CREATE: return 0U; case PROC_PHY_UPDATE: case PROC_CONN_UPDATE: @@ -267,6 +268,11 @@ void llcp_rr_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu * llcp_rp_comm_rx(conn, ctx, rx); break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +#if defined(CONFIG_BT_PERIPHERAL) && defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) + case PROC_CIS_CREATE: + llcp_rp_cc_rx(conn, ctx, rx); + break; +#endif /* CONFIG_BT_PERIPHERAL && CONFIG_BT_CTLR_PERIPHERAL_ISO */ #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) case PROC_CIS_TERMINATE: llcp_rp_comm_rx(conn, ctx, rx); @@ -362,6 +368,11 @@ static void rr_act_run(struct ll_conn *conn) llcp_rp_comm_run(conn, ctx, NULL); break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +#if defined(CONFIG_BT_PERIPHERAL) && defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) + case PROC_CIS_CREATE: + llcp_rp_cc_run(conn, ctx, NULL); + break; +#endif /* CONFIG_BT_PERIPHERAL && CONFIG_BT_CTLR_PERIPHERAL_ISO */ #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) case PROC_CIS_TERMINATE: llcp_rp_comm_run(conn, ctx, NULL); @@ -796,6 +807,9 @@ static const struct proc_role new_proc_lut[] = { #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) [PDU_DATA_LLCTRL_TYPE_CIS_TERMINATE_IND] = { PROC_CIS_TERMINATE, ACCEPT_ROLE_BOTH }, #endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */ +#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) + [PDU_DATA_LLCTRL_TYPE_CIS_REQ] = { PROC_CIS_CREATE, ACCEPT_ROLE_PERIPHERAL }, +#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ }; void llcp_rr_new(struct ll_conn *conn, struct node_rx_pdu *rx, bool valid_pdu) diff --git a/subsys/bluetooth/controller/ll_sw/ull_peripheral.c b/subsys/bluetooth/controller/ll_sw/ull_peripheral.c index d48093df3cd..26af85de95c 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_peripheral.c +++ b/subsys/bluetooth/controller/ll_sw/ull_peripheral.c @@ -218,6 +218,9 @@ void ull_periph_setup(struct node_rx_hdr *rx, struct node_rx_ftr *ftr, ull_cp_prt_reload_set(conn, conn_interval_us); #endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */ +#if (!defined(CONFIG_BT_LL_SW_LLCP_LEGACY)) + conn->connect_accept_to = DEFAULT_CONNECTION_ACCEPT_TIMEOUT_US; +#endif /* !defined(CONFIG_BT_LL_SW_LLCP_LEGACY) */ #if defined(CONFIG_BT_CTLR_LE_PING) /* APTO in no. of connection events */ conn->apto_reload = RADIO_CONN_EVENTS((30 * 1000 * 1000), diff --git a/subsys/bluetooth/controller/ll_sw/ull_peripheral_iso.c b/subsys/bluetooth/controller/ll_sw/ull_peripheral_iso.c index fe6acc50ded..1770d752026 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_peripheral_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_peripheral_iso.c @@ -26,9 +26,15 @@ #include "isoal.h" #include "ull_iso_types.h" +#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY) +#include "ull_tx_queue.h" +#endif + #include "ull_conn_types.h" #include "ull_conn_iso_types.h" #include "ull_internal.h" +#include "ull_llcp.h" +#include "ull_llcp_internal.h" #include "ull_conn_internal.h" #include "ull_conn_iso_internal.h" @@ -39,14 +45,19 @@ #include "common/log.h" #include "hal/debug.h" -uint8_t ll_cis_accept(uint16_t handle) +static struct ll_conn *ll_cis_get_acl_awaiting_reply(uint16_t handle, uint8_t *error) { struct ll_conn *acl_conn = NULL; for (int h = 0; h < CONFIG_BT_MAX_CONN; h++) { struct ll_conn *conn = ll_conn_get(h); +#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY) + uint16_t cis_handle = conn->llcp_cis.cis_handle; +#else + uint16_t cis_handle = ull_cp_cc_ongoing_handle(conn); +#endif - if (handle == conn->llcp_cis.cis_handle) { + if (handle == cis_handle) { /* ACL connection found */ acl_conn = conn; break; @@ -55,25 +66,62 @@ uint8_t ll_cis_accept(uint16_t handle) if (!acl_conn) { BT_ERR("No connection found for handle %u", handle); - return BT_HCI_ERR_UNKNOWN_CONN_ID; + *error = BT_HCI_ERR_UNKNOWN_CONN_ID; + return NULL; } if (acl_conn->lll.role == BT_CONN_ROLE_CENTRAL) { BT_ERR("Not allowed for central"); - return BT_HCI_ERR_CMD_DISALLOWED; + *error = BT_HCI_ERR_CMD_DISALLOWED; + return NULL; } - acl_conn->llcp_cis.req++; +#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY) + if (acl_conn->llcp_cis.state != LLCP_CIS_STATE_RSP_WAIT) { +#else + if (!ull_cp_cc_awaiting_reply(acl_conn)) { +#endif + BT_ERR("Not allowed in current procedure state"); + *error = BT_HCI_ERR_CMD_DISALLOWED; + return NULL; + } - return 0; + return acl_conn; +} + +uint8_t ll_cis_accept(uint16_t handle) +{ + uint8_t status = BT_HCI_ERR_SUCCESS; + struct ll_conn *acl_conn = ll_cis_get_acl_awaiting_reply(handle, &status); + + if (acl_conn) { + /* Accept request */ +#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY) + acl_conn->llcp_cis.req++; +#else + ull_cp_cc_accept(acl_conn); +#endif + } + + return status; } uint8_t ll_cis_reject(uint16_t handle, uint8_t reason) { - ARG_UNUSED(handle); - ARG_UNUSED(reason); + uint8_t status = BT_HCI_ERR_SUCCESS; - return BT_HCI_ERR_CMD_DISALLOWED; +#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY) + status = BT_HCI_ERR_CMD_DISALLOWED; +#else + struct ll_conn *acl_conn = ll_cis_reply_ok(handle, &status); + + if (acl_conn) { + /* Accept request */ + ull_cp_cc_reject(acl_conn, reason); + } +#endif + + return status; } int ull_peripheral_iso_init(void) diff --git a/tests/bluetooth/controller/common/defaults_cmake.txt b/tests/bluetooth/controller/common/defaults_cmake.txt index 1739e5d2866..28d6a8e4bdb 100644 --- a/tests/bluetooth/controller/common/defaults_cmake.txt +++ b/tests/bluetooth/controller/common/defaults_cmake.txt @@ -29,6 +29,7 @@ FILE(GLOB ll_sw_sources ${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c ${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c ${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_chmu.c + ${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c ${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp.c ${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_conn.c ${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ll_addr.c @@ -46,6 +47,7 @@ FILE(GLOB mock_sources ${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_peripheral_iso.c ${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull_central.c ${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull_scan.c ${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/lll_clock.c diff --git a/tests/bluetooth/controller/common/include/helper_pdu.h b/tests/bluetooth/controller/common/include/helper_pdu.h index bcc2e1e8717..4131b4d25a5 100644 --- a/tests/bluetooth/controller/common/include/helper_pdu.h +++ b/tests/bluetooth/controller/common/include/helper_pdu.h @@ -53,6 +53,10 @@ void helper_pdu_encode_cte_rsp(struct pdu_data *pdu, void *param); void helper_node_encode_cte_rsp(struct node_rx_pdu *rx, void *param); void helper_pdu_encode_zero(struct pdu_data *pdu, void *param); + +void helper_pdu_encode_cis_req(struct pdu_data *pdu, void *param); +void helper_pdu_encode_cis_rsp(struct pdu_data *pdu, void *param); +void helper_pdu_encode_cis_ind(struct pdu_data *pdu, void *param); void helper_pdu_encode_cis_terminate_ind(struct pdu_data *pdu, void *param); void helper_pdu_verify_ping_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param); @@ -135,6 +139,13 @@ void helper_node_verify_cte_rsp(const char *file, uint32_t line, struct node_rx_ void helper_pdu_ntf_verify_cte_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param); +void helper_node_verify_cis_request(const char *file, uint32_t line, struct node_rx_pdu *rx, + void *param); +void helper_node_verify_cis_established(const char *file, uint32_t line, struct node_rx_pdu *rx, + void *param); +void helper_pdu_verify_cis_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param); +void helper_pdu_verify_cis_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param); +void helper_pdu_verify_cis_ind(const char *file, uint32_t line, struct pdu_data *pdu, void *param); void helper_pdu_verify_cis_terminate_ind(const char *file, uint32_t line, struct pdu_data *pdu, void *param); @@ -167,6 +178,9 @@ enum helper_pdu_opcode { LL_LENGTH_RSP, LL_CTE_REQ, LL_CTE_RSP, + LL_CIS_REQ, + LL_CIS_RSP, + LL_CIS_IND, LL_CIS_TERMINATE_IND, LL_ZERO, }; @@ -176,6 +190,9 @@ enum helper_node_opcode { NODE_CONN_UPDATE, NODE_ENC_REFRESH, NODE_CTE_RSP, + NODE_CIS_REQUEST, + NODE_CIS_ESTABLISHED, + }; typedef void(helper_pdu_encode_func_t)(struct pdu_data *data, void *param); diff --git a/tests/bluetooth/controller/common/src/helper_pdu.c b/tests/bluetooth/controller/common/src/helper_pdu.c index 2f12e4e83f4..54ce5a2e920 100644 --- a/tests/bluetooth/controller/common/src/helper_pdu.c +++ b/tests/bluetooth/controller/common/src/helper_pdu.c @@ -407,6 +407,66 @@ void helper_node_encode_cte_rsp(struct node_rx_pdu *rx, void *param) rx->hdr.rx_ftr.iq_report = (struct cte_conn_iq_report *)param; } +void helper_pdu_encode_cis_req(struct pdu_data *pdu, void *param) +{ + struct pdu_data_llctrl_cis_req *p = param; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = offsetof(struct pdu_data_llctrl, cis_req) + + sizeof(struct pdu_data_llctrl_cis_req); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CIS_REQ; + + pdu->llctrl.cis_req.cig_id = p->cig_id; + pdu->llctrl.cis_req.cis_id = p->cis_id; + pdu->llctrl.cis_req.c_phy = p->c_phy; + pdu->llctrl.cis_req.p_phy = p->p_phy; + pdu->llctrl.cis_req.c_max_pdu = p->c_max_pdu; + pdu->llctrl.cis_req.p_max_pdu = p->p_max_pdu; + pdu->llctrl.cis_req.nse = p->nse; + pdu->llctrl.cis_req.p_bn = p->p_bn; + pdu->llctrl.cis_req.c_bn = p->c_bn; + pdu->llctrl.cis_req.c_ft = p->c_ft; + pdu->llctrl.cis_req.p_ft = p->p_ft; + pdu->llctrl.cis_req.iso_interval = p->iso_interval; + pdu->llctrl.cis_req.conn_event_count = p->conn_event_count; + memcpy(pdu->llctrl.cis_req.c_max_sdu_packed, p->c_max_sdu_packed, + sizeof(p->c_max_sdu_packed)); + memcpy(pdu->llctrl.cis_req.p_max_sdu, p->p_max_sdu, sizeof(p->p_max_sdu)); + memcpy(pdu->llctrl.cis_req.c_sdu_interval, p->c_sdu_interval, sizeof(p->c_sdu_interval)); + memcpy(pdu->llctrl.cis_req.p_sdu_interval, p->p_sdu_interval, sizeof(p->p_sdu_interval)); + memcpy(pdu->llctrl.cis_req.sub_interval, p->sub_interval, sizeof(p->sub_interval)); + memcpy(pdu->llctrl.cis_req.cis_offset_min, p->cis_offset_min, sizeof(p->cis_offset_min)); + memcpy(pdu->llctrl.cis_req.cis_offset_max, p->cis_offset_max, sizeof(p->cis_offset_max)); +} + +void helper_pdu_encode_cis_rsp(struct pdu_data *pdu, void *param) +{ + struct pdu_data_llctrl_cis_rsp *p = param; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = offsetof(struct pdu_data_llctrl, cis_rsp) + + sizeof(struct pdu_data_llctrl_cis_rsp); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CIS_RSP; + memcpy(pdu->llctrl.cis_rsp.cis_offset_min, p->cis_offset_min, sizeof(p->cis_offset_min)); + memcpy(pdu->llctrl.cis_rsp.cis_offset_max, p->cis_offset_max, sizeof(p->cis_offset_max)); + pdu->llctrl.cis_rsp.conn_event_count = p->conn_event_count; +} + +void helper_pdu_encode_cis_ind(struct pdu_data *pdu, void *param) +{ + struct pdu_data_llctrl_cis_ind *p = param; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = offsetof(struct pdu_data_llctrl, cis_ind) + + sizeof(struct pdu_data_llctrl_cis_ind); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CIS_IND; + memcpy(pdu->llctrl.cis_ind.aa, p->aa, sizeof(p->aa)); + memcpy(pdu->llctrl.cis_ind.cis_offset, p->cis_offset, sizeof(p->cis_offset)); + memcpy(pdu->llctrl.cis_ind.cig_sync_delay, p->cig_sync_delay, sizeof(p->cig_sync_delay)); + memcpy(pdu->llctrl.cis_ind.cis_sync_delay, p->cis_sync_delay, sizeof(p->cis_sync_delay)); + pdu->llctrl.cis_ind.conn_event_count = p->conn_event_count; +} + void helper_pdu_encode_cis_terminate_ind(struct pdu_data *pdu, void *param) { struct pdu_data_llctrl_cis_terminate_ind *p = param; @@ -983,6 +1043,144 @@ void helper_pdu_ntf_verify_cte_rsp(const char *file, uint32_t line, struct pdu_d "Not a LL_CTE_RSP. Called at %s:%d\n", file, line); } +void helper_node_verify_cis_request(const char *file, uint32_t line, struct node_rx_pdu *rx, + void *param) +{ + struct node_rx_conn_iso_req *p = (struct node_rx_conn_iso_req *)param; + struct node_rx_conn_iso_req *pdu = (struct node_rx_conn_iso_req *)rx->pdu; + + zassert_equal(rx->hdr.type, NODE_RX_TYPE_CIS_REQUEST, + "Not a CIS_REQUEST node.\nCalled at %s:%d\n", file, line); + zassert_equal(p->cig_id, pdu->cig_id, + "cig_id mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(p->cis_handle, pdu->cis_handle, + "cis_handle mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(p->cis_id, pdu->cis_id, + "cis_id mismatch.\nCalled at %s:%d\n", file, line); +} + +void helper_node_verify_cis_established(const char *file, uint32_t line, struct node_rx_pdu *rx, + void *param) +{ + struct node_rx_conn_iso_estab *p = (struct node_rx_conn_iso_estab *)param; + struct node_rx_conn_iso_estab *pdu = (struct node_rx_conn_iso_estab *)rx->pdu; + + zassert_equal(rx->hdr.type, NODE_RX_TYPE_CIS_ESTABLISHED, + "Not a CIS_ESTABLISHED node.\nCalled at %s:%d\n", file, line); + zassert_equal(p->status, pdu->status, + "status mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(p->cis_handle, pdu->cis_handle, + "cis_handle mismatch.\nCalled at %s:%d\n", file, line); +} + +void helper_pdu_verify_cis_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param) +{ + struct pdu_data_llctrl_cis_req *p = param; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = offsetof(struct pdu_data_llctrl, cis_req) + + sizeof(struct pdu_data_llctrl_cis_req); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CIS_REQ; + + zassert_equal(pdu->llctrl.cis_req.cig_id, p->cig_id, + "cig_id mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.cis_id, p->cis_id, + "cis_id mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.c_phy, p->c_phy, + "c_phy mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.p_phy, p->p_phy, + "p_phy mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.c_max_pdu, p->c_max_pdu, + "c_max_pdu mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.p_max_pdu, p->p_max_pdu, + "p_max_pdu mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.nse, p->nse, + "nse mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.p_bn, p->p_bn, + "p_bn mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.c_bn, p->c_bn, + "c_bn mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.c_ft, p->c_ft, + "c_ft mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.p_ft, p->p_ft, + "p_ft mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.iso_interval, p->iso_interval, + "iso_interval mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_req.conn_event_count, p->conn_event_count, + "conn_event_count mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_req.c_max_sdu_packed, p->c_max_sdu_packed, + sizeof(p->c_max_sdu_packed), + "c_max_sdu_packed mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_req.p_max_sdu, p->p_max_sdu, + sizeof(p->p_max_sdu), + "p_max_sdu mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_req.c_sdu_interval, p->c_sdu_interval, + sizeof(p->c_sdu_interval), + "c_sdu_interval mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_req.p_sdu_interval, p->p_sdu_interval, + sizeof(p->p_sdu_interval), + "p_sdu_interval mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_req.sub_interval, p->sub_interval, + sizeof(p->sub_interval), + "sub_interval mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_req.cis_offset_min, p->cis_offset_min, + sizeof(p->cis_offset_min), + "cis_offset_min mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_req.cis_offset_max, p->cis_offset_max, + sizeof(p->cis_offset_max), + "cis_offset_max mismatch.\nCalled at %s:%d\n", file, line); +} + +void helper_pdu_verify_cis_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param) +{ + struct pdu_data_llctrl_cis_rsp *p = param; + + zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file, + line); + zassert_equal(pdu->len, + offsetof(struct pdu_data_llctrl, cis_rsp) + + sizeof(struct pdu_data_llctrl_cis_rsp), + "Wrong length.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CIS_RSP, + "Not a LL_CIS_RSP.\nCalled at %s:%d\n", file, line); + + zassert_mem_equal(pdu->llctrl.cis_rsp.cis_offset_min, p->cis_offset_min, + sizeof(p->cis_offset_min), + "cis_offset_min mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_rsp.cis_offset_max, p->cis_offset_max, + sizeof(p->cis_offset_max), + "cis_offset_max mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.cis_rsp.conn_event_count, p->conn_event_count, + "conn_event_count mismatch.\nCalled at %s:%d\n", file, line); +} + +void helper_pdu_verify_cis_ind(const char *file, uint32_t line, struct pdu_data *pdu, void *param) +{ + struct pdu_data_llctrl_cis_ind *p = param; + + zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file, + line); + zassert_equal(pdu->len, + offsetof(struct pdu_data_llctrl, cis_ind) + + sizeof(struct pdu_data_llctrl_cis_ind), + "Wrong length.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CIS_IND, + "Not a LL_CIS_IND.\nCalled at %s:%d\n", file, line); + + zassert_mem_equal(pdu->llctrl.cis_ind.aa, p->aa, sizeof(p->aa), + "aa mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_ind.cis_offset, p->cis_offset, sizeof(p->cis_offset), + "cis_offset mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_ind.cig_sync_delay, p->cig_sync_delay, + sizeof(p->cig_sync_delay), + "cig_sync_delay mismatch.\nCalled at %s:%d\n", file, line); + zassert_mem_equal(pdu->llctrl.cis_ind.cis_sync_delay, p->cis_sync_delay, + sizeof(p->cis_sync_delay), + "cis_sync_delay mismatch.\nCalled at %s:%d\n", file, line); + + pdu->llctrl.cis_ind.conn_event_count = p->conn_event_count; +} + void helper_pdu_verify_cis_terminate_ind(const char *file, uint32_t line, struct pdu_data *pdu, void *param) { diff --git a/tests/bluetooth/controller/common/src/helper_util.c b/tests/bluetooth/controller/common/src/helper_util.c index 1c51e88cf31..c7ab53bb124 100644 --- a/tests/bluetooth/controller/common/src/helper_util.c +++ b/tests/bluetooth/controller/common/src/helper_util.c @@ -87,6 +87,9 @@ helper_pdu_encode_func_t *const helper_pdu_encode[] = { [LL_LENGTH_RSP] = helper_pdu_encode_length_rsp, [LL_CTE_REQ] = helper_pdu_encode_cte_req, [LL_CTE_RSP] = helper_pdu_encode_cte_rsp, + [LL_CIS_REQ] = helper_pdu_encode_cis_req, + [LL_CIS_RSP] = helper_pdu_encode_cis_rsp, + [LL_CIS_IND] = helper_pdu_encode_cis_ind, [LL_CIS_TERMINATE_IND] = helper_pdu_encode_cis_terminate_ind, [LL_ZERO] = helper_pdu_encode_zero, }; @@ -120,6 +123,9 @@ helper_pdu_verify_func_t *const helper_pdu_verify[] = { [LL_LENGTH_RSP] = helper_pdu_verify_length_rsp, [LL_CTE_REQ] = helper_pdu_verify_cte_req, [LL_CTE_RSP] = helper_pdu_verify_cte_rsp, + [LL_CIS_REQ] = helper_pdu_verify_cis_req, + [LL_CIS_RSP] = helper_pdu_verify_cis_rsp, + [LL_CIS_IND] = helper_pdu_verify_cis_ind, [LL_CIS_TERMINATE_IND] = helper_pdu_verify_cis_terminate_ind, }; @@ -151,6 +157,9 @@ helper_pdu_ntf_verify_func_t *const helper_pdu_ntf_verify[] = { [LL_CTE_REQ] = NULL, [LL_CTE_RSP] = helper_pdu_ntf_verify_cte_rsp, [LL_CTE_RSP] = NULL, + [LL_CIS_REQ] = NULL, + [LL_CIS_RSP] = NULL, + [LL_CIS_IND] = NULL, [LL_CIS_TERMINATE_IND] = NULL, }; @@ -179,6 +188,9 @@ helper_node_encode_func_t *const helper_node_encode[] = { [LL_CHAN_MAP_UPDATE_IND] = NULL, [LL_CTE_REQ] = NULL, [LL_CTE_RSP] = helper_node_encode_cte_rsp, + [LL_CIS_REQ] = NULL, + [LL_CIS_RSP] = NULL, + [LL_CIS_IND] = NULL, [LL_CIS_TERMINATE_IND] = NULL, }; @@ -187,6 +199,8 @@ helper_node_verify_func_t *const helper_node_verify[] = { [NODE_CONN_UPDATE] = helper_node_verify_conn_update, [NODE_ENC_REFRESH] = helper_node_verify_enc_refresh, [NODE_CTE_RSP] = helper_node_verify_cte_rsp, + [NODE_CIS_REQUEST] = helper_node_verify_cis_request, + [NODE_CIS_ESTABLISHED] = helper_node_verify_cis_established, }; /* diff --git a/tests/bluetooth/controller/ctrl_cis_create/CMakeLists.txt b/tests/bluetooth/controller/ctrl_cis_create/CMakeLists.txt new file mode 100644 index 00000000000..cf25ce456bc --- /dev/null +++ b/tests/bluetooth/controller/ctrl_cis_create/CMakeLists.txt @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +if (NOT BOARD STREQUAL unit_testing) + message(FATAL_ERROR "This project can only be used with '-DBOARD=unit_testing'.") +endif() + +FILE(GLOB SOURCES + src/*.c +) + +project(bluetooth_ull_llcp_cis_create) +find_package(ZephyrUnittest HINTS $ENV{ZEPHYR_BASE}) +include(${ZEPHYR_BASE}/tests/bluetooth/controller/common/defaults_cmake.txt) +target_sources(testbinary PRIVATE ${ll_sw_sources} ${mock_sources} ${common_sources}) diff --git a/tests/bluetooth/controller/ctrl_cis_create/src/main.c b/tests/bluetooth/controller/ctrl_cis_create/src/main.c new file mode 100644 index 00000000000..aee02b32e19 --- /dev/null +++ b/tests/bluetooth/controller/ctrl_cis_create/src/main.c @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2022 Demant + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "kconfig.h" + +#include +#include +#include +#include +#include "hal/ccm.h" + +#include "util/util.h" +#include "util/mem.h" +#include "util/memq.h" +#include "util/dbuf.h" + +#include "pdu.h" +#include "ll.h" +#include "ll_settings.h" + +#include "lll.h" +#include "lll_df_types.h" +#include "lll_conn_iso.h" +#include "lll_conn.h" + +#include "ull_tx_queue.h" + +#include "isoal.h" +#include "ull_iso_types.h" +#include "ull_conn_types.h" +#include "ull_conn_iso_types.h" +#include "ull_llcp.h" +#include "ull_conn_internal.h" +#include "ull_llcp_internal.h" + +#include "helper_pdu.h" +#include "helper_util.h" + +struct ll_conn conn; + +static void setup(void) +{ + test_setup(&conn); +} + +static bool is_instant_reached(struct ll_conn *conn, uint16_t instant) +{ + return ((event_counter(conn) - instant) & 0xFFFF) <= 0x7FFF; +} + +static struct pdu_data_llctrl_cis_req remote_cis_req = { + .cig_id = 0x01, + .cis_id = 0x02, + .c_phy = 0x01, + .p_phy = 0x01, + .c_max_sdu_packed = { 0, 160}, + .p_max_sdu = { 0, 160}, + .c_max_pdu = 160, + .p_max_pdu = 160, + .nse = 2, + .p_bn = 1, + .c_bn = 1, + .c_ft = 1, + .p_ft = 1, + .iso_interval = 6, + .conn_event_count = 12, + .c_sdu_interval = { 0, 0, 0}, + .p_sdu_interval = { 0, 0, 0}, + .sub_interval = { 0, 0, 0}, + .cis_offset_min = { 0, 0, 0}, + .cis_offset_max = { 0, 0, 0} +}; + +static struct pdu_data_llctrl_cis_ind remote_cis_ind = { + .aa = { 0, 0, 0, 0}, + .cig_sync_delay = { 0, 0, 0}, + .cis_offset = { 0, 0, 0}, + .cis_sync_delay = { 0, 0, 0}, + .conn_event_count = 12 +}; + +#define ERROR_CODE 0x17 +/* + * Central-initiated CIS Create procedure. + * Central requests CIS, peripheral Host rejects. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_S | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CIS_REQ | + * | |<--------------------------| + * | | | + * | LE CIS Request | | + * |<--------------------------| | + * | LE CIS Request | | + * | Accept | | + * |-------------------------->| | + * | | | + * | | LL_CIS_RSP | + * | |-------------------------->| + * | | | + * | | LL_CIS_IND | + * | |<--------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * | | | + * | LE CIS ESTABLISHED | | + * |<--------------------------| | + */ +static void test_cc_create_periph_rem_host_accept(void) +{ + struct node_tx *tx; + struct node_rx_pdu *ntf; + struct node_rx_conn_iso_req cis_req = { + .cig_id = 0x01, + .cis_handle = 0x00, + .cis_id = 0x02 + }; + struct pdu_data_llctrl_cis_rsp local_cis_rsp = { + .cis_offset_max = { 0, 0, 0}, + .cis_offset_min = { 0, 0, 0}, + .conn_event_count = 12 + }; + struct node_rx_conn_iso_estab cis_estab = { + .cis_handle = 0x00, + .status = 0x00 + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_CIS_REQ, &conn, &remote_cis_req); + + /* Done */ + event_done(&conn); + + /* There should be excactly one host notification */ + ut_rx_node(NODE_CIS_REQUEST, &ntf, &cis_req); + ut_rx_q_is_empty(); + + /* Release Ntf */ + ull_cp_release_ntf(ntf); + + /* Accept request */ + ull_cp_cc_accept(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CIS_RSP, &conn, &tx, &local_cis_rsp); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Rx */ + lt_tx(LL_CIS_IND, &conn, &remote_cis_ind); + + /* Done */ + event_done(&conn); + + /* */ + while (!is_instant_reached(&conn, remote_cis_ind.conn_event_count)) { + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should NOT be a host notification */ + ut_rx_q_is_empty(); + } + + /* Emulate CIS becoming established */ + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should NOT be a host notification */ + ut_rx_q_is_empty(); + + /* Prepare */ + event_prepare(&conn); + + /* There should be excactly one host notification */ + ut_rx_node(NODE_CIS_ESTABLISHED, &ntf, &cis_estab); + ut_rx_q_is_empty(); + + /* Done */ + event_done(&conn); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + +/* + * Central-initiated CIS Create procedure. + * Central requests CIS, peripheral Host rejects. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_S | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CIS_REQ | + * | |<--------------------------| + * | | | + * | LE CIS Request | | + * |<--------------------------| | + * | LE CIS Request | | + * | Decline | | + * |-------------------------->| | + * | | | + * | | LL_REJECT_EXT_IND | + * | |-------------------------->| + * | | | + */ +static void test_cc_create_periph_rem_host_reject(void) +{ + struct node_tx *tx; + struct node_rx_pdu *ntf; + struct node_rx_conn_iso_req cis_req = { + .cig_id = 0x01, + .cis_handle = 0x00, + .cis_id = 0x02 + }; + struct pdu_data_llctrl_reject_ext_ind local_reject = { + .error_code = ERROR_CODE, + .reject_opcode = PDU_DATA_LLCTRL_TYPE_CIS_REQ + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_CIS_REQ, &conn, &remote_cis_req); + + /* Done */ + event_done(&conn); + + /* There should be excactly one host notification */ + ut_rx_node(NODE_CIS_REQUEST, &ntf, &cis_req); + ut_rx_q_is_empty(); + + /* Decline request */ + ull_cp_cc_reject(&conn, ERROR_CODE); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_REJECT_EXT_IND, &conn, &tx, &local_reject); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + +/* + * Central-initiated CIS Create procedure. + * Central requests CIS, ask peripheral host peripheral Host fails to reply + * + * +-----+ +-------+ +-----+ + * | UT | | LL_S | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CIS_REQ | + * | |<--------------------------| + * | | | + * | LE CIS Request | | + * |<--------------------------| | + * | | | + * + * + * Let time pass ...... + * + * + * | | | + * | | LL_REJECT_EXT_IND (to) | + * | |-------------------------->| + * | | | + */ +static void test_cc_create_periph_rem_host_accept_to(void) +{ + struct node_tx *tx; + struct node_rx_pdu *ntf; + struct node_rx_conn_iso_req cis_req = { + .cig_id = 0x01, + .cis_handle = 0x00, + .cis_id = 0x02 + }; + struct pdu_data_llctrl_reject_ext_ind local_reject = { + .error_code = BT_HCI_ERR_CONN_ACCEPT_TIMEOUT, + .reject_opcode = PDU_DATA_LLCTRL_TYPE_CIS_REQ + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_CIS_REQ, &conn, &remote_cis_req); + + /* Done */ + event_done(&conn); + + /* There should be excactly one host notification */ + ut_rx_node(NODE_CIS_REQUEST, &ntf, &cis_req); + ut_rx_q_is_empty(); + + /* Emulate that time passes real fast re. timeout */ + conn.connect_accept_to = 0; + + /* Prepare */ + event_prepare(&conn); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should now have one LL Control PDU */ + lt_rx(LL_REJECT_EXT_IND, &conn, &tx, &local_reject); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + +/* + * Central-initiated CIS Create procedure. + * Central requests CIS w. invalid PHY param, peripheral Host rejects. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_S | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CIS_REQ (w. invald PHY) | + * | |<------------------------------| + * | | | + * | | | + * | | | + * | | | + * | | | + * | | | + * | | LL_REJECT_EXT_IND | + * | |------------------------------>| + * | | | + */ +static void test_cc_create_periph_rem_invalid_phy(void) +{ + static struct pdu_data_llctrl_cis_req remote_cis_req_invalid_phy = { + .cig_id = 0x01, + .cis_id = 0x02, + .c_phy = 0x03, + .p_phy = 0x01, + .c_max_sdu_packed = { 0, 160}, + .p_max_sdu = { 0, 160}, + .c_max_pdu = 160, + .p_max_pdu = 160, + .nse = 2, + .p_bn = 1, + .c_bn = 1, + .c_ft = 1, + .p_ft = 1, + .iso_interval = 6, + .conn_event_count = 12, + .c_sdu_interval = { 0, 0, 0}, + .p_sdu_interval = { 0, 0, 0}, + .sub_interval = { 0, 0, 0}, + .cis_offset_min = { 0, 0, 0}, + .cis_offset_max = { 0, 0, 0} + }; + struct node_tx *tx; + struct pdu_data_llctrl_reject_ext_ind local_reject = { + .error_code = BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL, + .reject_opcode = PDU_DATA_LLCTRL_TYPE_CIS_REQ + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_CIS_REQ, &conn, &remote_cis_req_invalid_phy); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_REJECT_EXT_IND, &conn, &tx, &local_reject); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + +void test_main(void) +{ + ztest_test_suite( + cis_create, + ztest_unit_test_setup_teardown(test_cc_create_periph_rem_host_accept, setup, + unit_test_noop), + ztest_unit_test_setup_teardown(test_cc_create_periph_rem_host_reject, setup, + unit_test_noop), + ztest_unit_test_setup_teardown(test_cc_create_periph_rem_host_accept_to, setup, + unit_test_noop), + ztest_unit_test_setup_teardown(test_cc_create_periph_rem_invalid_phy, setup, + unit_test_noop)); + + ztest_run_test_suite(cis_create); +} diff --git a/tests/bluetooth/controller/ctrl_cis_create/testcase.yaml b/tests/bluetooth/controller/ctrl_cis_create/testcase.yaml new file mode 100644 index 00000000000..24e87f315b2 --- /dev/null +++ b/tests/bluetooth/controller/ctrl_cis_create/testcase.yaml @@ -0,0 +1,5 @@ +common: + tags: test_framework bluetooth bt_cis_create bt_ull_llcp +tests: + bluetooth.controller.ctrl_cis_create.test: + type: unit diff --git a/tests/bluetooth/controller/ctrl_unsupported/src/main.c b/tests/bluetooth/controller/ctrl_unsupported/src/main.c index 65ca396f46e..fd35186fe6d 100644 --- a/tests/bluetooth/controller/ctrl_unsupported/src/main.c +++ b/tests/bluetooth/controller/ctrl_unsupported/src/main.c @@ -35,6 +35,7 @@ #include "ull_conn_iso_types.h" #include "ull_conn_types.h" #include "ull_llcp.h" +#include "ull_conn_internal.h" #include "ull_llcp_internal.h" #include "helper_pdu.h" diff --git a/tests/bluetooth/controller/mock_ctrl/src/ull_peripheral_iso.c b/tests/bluetooth/controller/mock_ctrl/src/ull_peripheral_iso.c new file mode 100644 index 00000000000..b92c0d1fc83 --- /dev/null +++ b/tests/bluetooth/controller/mock_ctrl/src/ull_peripheral_iso.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 Demant + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "util/util.h" +#include "util/memq.h" +#include "util/dbuf.h" +#include "util/mayfly.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_df_types.h" +#include "lll_conn.h" +#include "lll_conn_iso.h" + +#include "ull_tx_queue.h" + +#include "isoal.h" +#include "ull_iso_types.h" + +#include "ull_conn_types.h" +#include "ull_conn_iso_types.h" +#include "ull_internal.h" + +#include "ull_conn_internal.h" +#include "ull_conn_iso_internal.h" +#include "lll_peripheral_iso.h" + +uint8_t ull_peripheral_iso_acquire(struct ll_conn *acl, + struct pdu_data_llctrl_cis_req *req, + uint16_t *cis_handle) +{ + return 0; +} + +uint8_t ull_peripheral_iso_setup(struct pdu_data_llctrl_cis_ind *ind, + uint8_t cig_id, uint16_t cis_handle) +{ + return 0; +} + +void ull_peripheral_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire, + uint16_t cis_handle) +{ +}