diff --git a/subsys/bluetooth/controller/ll_sw/ull_central_iso_internal.h b/subsys/bluetooth/controller/ll_sw/ull_central_iso_internal.h index ea8a2e2f8ee..afec5ce5fe2 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_central_iso_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_central_iso_internal.h @@ -7,3 +7,14 @@ /* Helper functions to initialize and reset ull_central_iso module */ int ull_central_iso_init(void); int ull_central_iso_reset(void); + +void ull_central_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire, uint16_t cis_handle); +uint16_t ull_central_iso_cis_offset_get(struct ll_conn_iso_stream *cis, uint32_t *cis_offset_min, + uint32_t *cis_offset_max); +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); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.c b/subsys/bluetooth/controller/ll_sw/ull_llcp.c index dcccee29765..032c669b3c7 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.c @@ -35,6 +35,7 @@ #include "ull_iso_types.h" #include "ull_conn_iso_types.h" #include "ull_conn_iso_internal.h" +#include "ull_central_iso_internal.h" #include "ull_internal.h" #include "ull_conn_types.h" @@ -371,6 +372,11 @@ struct proc_ctx *llcp_create_local_procedure(enum llcp_proc proc) llcp_lp_comm_init_proc(ctx); break; #endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ +#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) + case PROC_CIS_CREATE: + llcp_lp_comm_init_proc(ctx); + break; +#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) */ #if defined(CONFIG_BT_CTLR_SCA_UPDATE) case PROC_SCA_UPDATE: llcp_lp_comm_init_proc(ctx); @@ -861,6 +867,53 @@ uint8_t ull_cp_cis_terminate(struct ll_conn *conn, } #endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ +#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) +uint8_t ull_cp_cis_create(struct ll_conn *conn, struct ll_conn_iso_stream *cis) +{ + struct ll_conn_iso_group *cig; + struct proc_ctx *ctx; + + if (conn->lll.handle != cis->lll.acl_handle) { + return BT_HCI_ERR_CMD_DISALLOWED; + } + + ctx = llcp_create_local_procedure(PROC_CIS_CREATE); + if (!ctx) { + return BT_HCI_ERR_CMD_DISALLOWED; + } + + cig = cis->group; + ctx->data.cis_create.cis_handle = cis->lll.handle; + + ctx->data.cis_create.cig_id = cis->group->cig_id; + ctx->data.cis_create.cis_id = cis->cis_id; + ctx->data.cis_create.c_phy = cis->lll.tx.phy; + ctx->data.cis_create.p_phy = cis->lll.rx.phy; + ctx->data.cis_create.c_sdu_interval = cig->c_sdu_interval; + ctx->data.cis_create.p_sdu_interval = cig->p_sdu_interval; + ctx->data.cis_create.c_max_pdu = cis->lll.tx.max_octets; + ctx->data.cis_create.p_max_pdu = cis->lll.rx.max_octets; + ctx->data.cis_create.c_max_sdu = cis->c_max_sdu; + ctx->data.cis_create.p_max_sdu = cis->p_max_sdu; + ctx->data.cis_create.iso_interval = cig->iso_interval; + ctx->data.cis_create.framed = cis->framed; + ctx->data.cis_create.nse = cis->lll.num_subevents; + ctx->data.cis_create.sub_interval = cis->lll.sub_interval; + ctx->data.cis_create.c_bn = cis->lll.tx.burst_number; + ctx->data.cis_create.p_bn = cis->lll.rx.burst_number; + ctx->data.cis_create.c_ft = cis->lll.tx.flush_timeout; + ctx->data.cis_create.p_ft = cis->lll.rx.flush_timeout; + + ctx->data.cis_create.conn_event_count = + ull_central_iso_cis_offset_get(cis, &ctx->data.cis_create.cis_offset_min, + &ctx->data.cis_create.cis_offset_max); + + llcp_lr_enqueue(conn, ctx); + + return BT_HCI_ERR_SUCCESS; +} +#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) */ + #if defined(CONFIG_BT_CENTRAL) uint8_t ull_cp_chan_map_update(struct ll_conn *conn, const uint8_t chm[5]) { @@ -1226,6 +1279,19 @@ void ull_cp_cc_reject(struct ll_conn *conn, uint8_t error_code) } #endif /* defined(CONFIG_BT_PERIPHERAL) && defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ +#if defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_CTLR_CENTRAL_ISO) +bool ull_lp_cc_is_active(struct ll_conn *conn) +{ + struct proc_ctx *ctx; + + ctx = llcp_lr_peek(conn); + if (ctx && ctx->proc == PROC_CIS_CREATE) { + return llcp_lp_cc_is_active(ctx); + } + return false; +} +#endif /* defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_CTLR_CENTRAL_ISO) */ + static bool pdu_is_expected(struct pdu_data *pdu, struct proc_ctx *ctx) { return (ctx->rx_opcode == pdu->llctrl.opcode || ctx->rx_greedy); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.h b/subsys/bluetooth/controller/ll_sw/ull_llcp.h index 3ed1c91a002..27f84616138 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.h @@ -181,6 +181,11 @@ 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 Initiate a CIS Create Procedure. + */ +uint8_t ull_cp_cis_create(struct ll_conn *conn, struct ll_conn_iso_stream *cis); + /** * @brief Is ongoing create cis procedure expecting a reply? */ @@ -202,6 +207,16 @@ void ull_cp_cc_accept(struct ll_conn *conn); */ void ull_cp_cc_reject(struct ll_conn *conn, uint8_t error_code); +/** + * @brief CIS was established. + */ +void ull_cp_cc_established(struct ll_conn *conn, uint8_t error_code); + +/** + * @brief CIS creation ongoing. + */ +bool ull_lp_cc_is_active(struct ll_conn *conn); + /** * @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 index 1dbc35d7b88..12dad1c7cff 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c @@ -44,6 +44,7 @@ #include "ull_iso_internal.h" #include "ull_conn_iso_internal.h" #include "ull_peripheral_iso_internal.h" +#include "ull_central_iso_internal.h" #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) #define LOG_MODULE_NAME bt_ctlr_ull_llcp_cis @@ -51,6 +52,37 @@ #include #include "hal/debug.h" +static bool 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 cc_ntf_established(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(); +} + #if defined(CONFIG_BT_PERIPHERAL) static uint16_t cc_event_counter(struct ll_conn *conn) { @@ -109,6 +141,7 @@ enum { /* Unknown response received */ RP_CC_EVT_UNKNOWN, }; + /* * LLCP Remote Procedure FSM */ @@ -174,36 +207,12 @@ static void rp_cc_ntf_create(struct ll_conn *conn, struct proc_ctx *ctx) 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); + cc_ntf_established(conn, ctx); llcp_rr_complete(conn); ctx->state = RP_CC_STATE_IDLE; } @@ -296,14 +305,6 @@ static uint8_t rp_cc_check_phy(struct ll_conn *conn, struct proc_ctx *ctx, 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) { @@ -495,7 +496,7 @@ static void rp_cc_state_wait_cis_established(struct ll_conn *conn, struct proc_c switch (evt) { case RP_CC_EVT_RUN: /* Check for CIS state */ - if (rp_cc_check_cis_established_lll(ctx)) { + if (cc_check_cis_established_lll(ctx)) { /* CIS was established, so let's got ahead and complete procedure */ rp_cc_complete(conn, ctx, evt, param); } @@ -599,3 +600,312 @@ 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 */ + +#if defined(CONFIG_BT_CENTRAL) +static void lp_cc_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param); + +/* LLCP Local Procedure FSM states */ +enum { + LP_CC_STATE_IDLE, + LP_CC_STATE_WAIT_TX_CIS_REQ, + LP_CC_STATE_WAIT_RX_CIS_RSP, + LP_CC_STATE_WAIT_TX_CIS_IND, + LP_CC_STATE_WAIT_INSTANT, + LP_CC_STATE_WAIT_ESTABLISHED, + LP_CC_STATE_WAIT_NTF, +}; + +/* LLCP Local Procedure CIS Creation FSM events */ +enum { + /* Procedure run */ + LP_CC_EVT_RUN, + + /* Response received */ + LP_CC_EVT_CIS_RSP, + + /* Reject response received */ + LP_CC_EVT_REJECT, + + /* Unknown response received */ + LP_CC_EVT_UNKNOWN, +}; + +static void lp_cc_tx(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 */ + switch (opcode) { + case PDU_DATA_LLCTRL_TYPE_CIS_REQ: + llcp_pdu_encode_cis_req(ctx, pdu); + break; + case PDU_DATA_LLCTRL_TYPE_CIS_IND: + llcp_pdu_encode_cis_ind(ctx, pdu); + break; + default: + /* Unknown opcode */ + LL_ASSERT(0); + break; + } + + ctx->tx_opcode = pdu->llctrl.opcode; + + /* Enqueue LL Control PDU towards LLL */ + llcp_tx_enqueue(conn, tx); +} + +void llcp_lp_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_RSP: + lp_cc_execute_fsm(conn, ctx, LP_CC_EVT_CIS_RSP, pdu); + break; + case PDU_DATA_LLCTRL_TYPE_REJECT_IND: + case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND: + lp_cc_execute_fsm(conn, ctx, LP_CC_EVT_REJECT, pdu); + break; + default: + /* Unknown opcode */ + LL_ASSERT(0); + } +} + +static void lp_cc_send_cis_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + if (llcp_lr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { + ctx->state = LP_CC_STATE_WAIT_TX_CIS_REQ; + } else { + lp_cc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CIS_REQ); + + ctx->state = LP_CC_STATE_WAIT_RX_CIS_RSP; + ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CIS_RSP; + } +} + +static void lp_cc_st_wait_tx_cis_req(struct ll_conn *conn, struct proc_ctx *ctx, + uint8_t evt, void *param) +{ + switch (evt) { + case LP_CC_EVT_RUN: + lp_cc_send_cis_req(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void lp_cc_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) +{ + switch (evt) { + case LP_CC_EVT_RUN: + switch (ctx->proc) { + case PROC_CIS_CREATE: + lp_cc_send_cis_req(conn, ctx, evt, param); + break; + default: + /* Unknown procedure */ + LL_ASSERT(0); + break; + } + break; + default: + /* Ignore other evts */ + break; + } +} + +static void cc_prepare_cis_ind(struct ll_conn *conn, struct proc_ctx *ctx) +{ + uint8_t err; + + /* Setup central parameters based on CIS_RSP */ + err = ull_central_iso_setup(ctx->data.cis_create.cis_handle, + &ctx->data.cis_create.cig_sync_delay, + &ctx->data.cis_create.cis_sync_delay, + &ctx->data.cis_create.cis_offset_min, + &ctx->data.cis_create.cis_offset_max, + &ctx->data.cis_create.conn_event_count, + ctx->data.cis_create.aa); + LL_ASSERT(!err); + + ctx->state = LP_CC_STATE_WAIT_INSTANT; + ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_UNUSED; +} + +static void lp_cc_send_cis_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + if (llcp_lr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { + ctx->state = LP_CC_STATE_WAIT_TX_CIS_IND; + } else { + cc_prepare_cis_ind(conn, ctx); + lp_cc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CIS_IND); + } +} + +static void lp_cc_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) +{ + if (!llcp_ntf_alloc_is_available()) { + ctx->state = LP_CC_STATE_WAIT_NTF; + } else { + cc_ntf_established(conn, ctx); + llcp_lr_complete(conn); + ctx->state = LP_CC_STATE_IDLE; + } +} + +static void lp_cc_st_wait_rx_cis_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + struct pdu_data *pdu = (struct pdu_data *)param; + + switch (evt) { + case LP_CC_EVT_CIS_RSP: + /* TODO: Reject response if outside offset range? */ + llcp_pdu_decode_cis_rsp(ctx, param); + lp_cc_send_cis_ind(conn, ctx, evt, param); + break; + case LP_CC_EVT_UNKNOWN: + /* Unsupported in peer, so disable locally for this connection */ + feature_unmask_features(conn, LL_FEAT_BIT_CIS_PERIPHERAL); + ctx->data.cis_create.error = BT_HCI_ERR_UNSUPP_REMOTE_FEATURE; + lp_cc_complete(conn, ctx, evt, param); + break; + case LP_CC_EVT_REJECT: + if (pdu->llctrl.reject_ext_ind.error_code == BT_HCI_ERR_UNSUPP_REMOTE_FEATURE) { + /* Unsupported in peer, so disable locally for this connection */ + feature_unmask_features(conn, LL_FEAT_BIT_CIS_PERIPHERAL); + } + ctx->data.cis_create.error = pdu->llctrl.reject_ext_ind.error_code; + lp_cc_complete(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void lp_cc_st_wait_tx_cis_ind(struct ll_conn *conn, struct proc_ctx *ctx, + uint8_t evt, void *param) +{ + switch (evt) { + case LP_CC_EVT_RUN: + lp_cc_send_cis_ind(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void lp_cc_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + uint16_t event_counter = ull_conn_event_counter(conn); + + if (is_instant_reached_or_passed(ctx->data.cis_create.conn_event_count, event_counter)) { + /* Start CIS */ + ull_central_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 = LP_CC_STATE_WAIT_ESTABLISHED; + + /* Stop procedure response timeout timer */ + llcp_lr_prt_stop(conn); + } +} + +static void lp_cc_st_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case LP_CC_EVT_RUN: + lp_cc_check_instant(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void lp_cc_st_wait_established(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case LP_CC_EVT_RUN: + if (cc_check_cis_established_lll(ctx)) { + /* CIS was established, so let's got ahead and complete procedure */ + lp_cc_complete(conn, ctx, evt, param); + } + break; + default: + /* Ignore other evts */ + break; + } +} + +static void lp_cc_st_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) +{ + switch (evt) { + case LP_CC_EVT_RUN: + lp_cc_complete(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void lp_cc_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) +{ + switch (ctx->state) { + case LP_CC_STATE_IDLE: + lp_cc_st_idle(conn, ctx, evt, param); + break; + case LP_CC_STATE_WAIT_TX_CIS_REQ: + lp_cc_st_wait_tx_cis_req(conn, ctx, evt, param); + break; + case LP_CC_STATE_WAIT_RX_CIS_RSP: + lp_cc_st_wait_rx_cis_rsp(conn, ctx, evt, param); + break; + case LP_CC_STATE_WAIT_TX_CIS_IND: + lp_cc_st_wait_tx_cis_ind(conn, ctx, evt, param); + break; + case LP_CC_STATE_WAIT_INSTANT: + lp_cc_st_wait_instant(conn, ctx, evt, param); + break; + case LP_CC_STATE_WAIT_ESTABLISHED: + lp_cc_st_wait_established(conn, ctx, evt, param); + break; + case LP_CC_STATE_WAIT_NTF: + lp_cc_st_wait_ntf(conn, ctx, evt, param); + break; + default: + /* Unknown state */ + LL_ASSERT(0); + break; + } +} + +void llcp_lp_cc_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param) +{ + lp_cc_execute_fsm(conn, ctx, LP_CC_EVT_RUN, param); +} + +bool llcp_lp_cc_is_active(struct proc_ctx *ctx) +{ + return ctx->state != LP_CC_STATE_IDLE; +} +#endif /* CONFIG_BT_CENTRAL */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h index b2db551a045..a8673057eb3 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h @@ -159,6 +159,26 @@ static inline bool feature_peer_smi_tx(struct ll_conn *conn) return (conn->llcp.fex.features_peer & BIT64(BT_LE_FEAT_BIT_SMI_TX)) != 0; } +static inline bool feature_peer_iso_central(struct ll_conn *conn) +{ + return (conn->llcp.fex.features_peer & LL_FEAT_BIT_CIS_CENTRAL) != 0; +} + +static inline bool feature_iso_central(struct ll_conn *conn) +{ + return LL_FEAT_BIT_CIS_CENTRAL != 0; +} + +static inline bool feature_peer_iso_peripheral(struct ll_conn *conn) +{ + return (conn->llcp.fex.features_peer & LL_FEAT_BIT_CIS_PERIPHERAL) != 0; +} + +static inline bool feature_iso_peripheral(struct ll_conn *conn) +{ + return LL_FEAT_BIT_CIS_PERIPHERAL != 0; +} + /* * The following features are not yet defined in KConfig and do * not have a bitfield defined in ll_feat.h diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h index 538b1354218..ca12a7a2328 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h @@ -264,9 +264,8 @@ struct proc_ctx { 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) - */ + uint32_t cig_sync_delay; + uint32_t cis_sync_delay; uint8_t c_phy; uint8_t p_phy; uint16_t c_max_sdu; @@ -283,6 +282,7 @@ struct proc_ctx { uint8_t c_ft; uint8_t p_ft; uint16_t iso_interval; + uint8_t aa[4]; #endif /* defined(CONFIG_BT_CENTRAL) */ } cis_create; @@ -705,6 +705,7 @@ void llcp_pdu_encode_cte_rsp(const struct proc_ctx *ctx, struct pdu_data *pdu); 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); +bool llcp_lp_cc_is_active(struct proc_ctx *ctx); 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); @@ -717,6 +718,9 @@ 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); +void llcp_pdu_encode_cis_req(struct proc_ctx *ctx, struct pdu_data *pdu); +void llcp_pdu_encode_cis_ind(struct proc_ctx *ctx, struct pdu_data *pdu); +void llcp_pdu_decode_cis_rsp(struct proc_ctx *ctx, struct pdu_data *pdu); #ifdef ZTEST_UNITTEST bool lr_is_disconnected(struct ll_conn *conn); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c index c3a8847a4f3..bfb4fc5b579 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c @@ -201,6 +201,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 /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ +#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) + case PROC_CIS_CREATE: + llcp_lp_cc_rx(conn, ctx, rx); + break; +#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) */ #if defined(CONFIG_BT_CTLR_SCA_UPDATE) case PROC_SCA_UPDATE: llcp_lp_comm_rx(conn, ctx, rx); @@ -321,6 +326,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) + case PROC_CIS_CREATE: + llcp_lp_cc_run(conn, ctx, NULL); + break; +#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) case PROC_CIS_TERMINATE: llcp_lp_comm_run(conn, ctx, NULL); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c index 947f037d107..f5fbb0583f0 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c @@ -845,6 +845,92 @@ void llcp_pdu_encode_cis_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) 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) +void llcp_pdu_encode_cis_req(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + struct pdu_data_llctrl_cis_req *p; + + 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; + + p = &pdu->llctrl.cis_req; + + p->cig_id = ctx->data.cis_create.cig_id; + p->cis_id = ctx->data.cis_create.cis_id; + p->c_phy = ctx->data.cis_create.c_phy; + p->p_phy = ctx->data.cis_create.p_phy; + p->nse = ctx->data.cis_create.nse; + p->c_bn = ctx->data.cis_create.c_bn; + p->p_bn = ctx->data.cis_create.p_bn; + p->c_ft = ctx->data.cis_create.c_ft; + p->p_ft = ctx->data.cis_create.p_ft; + + sys_put_le24(ctx->data.cis_create.c_sdu_interval, p->c_sdu_interval); + sys_put_le24(ctx->data.cis_create.p_sdu_interval, p->p_sdu_interval); + 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); + sys_put_le24(ctx->data.cis_create.sub_interval, p->sub_interval); + + p->c_max_pdu = sys_cpu_to_le16(ctx->data.cis_create.c_max_pdu); + p->p_max_pdu = sys_cpu_to_le16(ctx->data.cis_create.p_max_pdu); + p->iso_interval = sys_cpu_to_le16(ctx->data.cis_create.iso_interval); + p->conn_event_count = sys_cpu_to_le16(ctx->data.cis_create.conn_event_count); + + p->c_max_sdu_packed[0] = ctx->data.cis_create.c_max_sdu & 0xFF; + p->c_max_sdu_packed[1] = ((ctx->data.cis_create.c_max_sdu >> 8) & 0x0F) | + (ctx->data.cis_create.framed << 7); + p->p_max_sdu[0] = ctx->data.cis_create.p_max_sdu & 0xFF; + p->p_max_sdu[1] = (ctx->data.cis_create.p_max_sdu >> 8) & 0x0F; +} + +void llcp_pdu_decode_cis_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + /* Limit response to valid range */ + uint32_t cis_offset_min = sys_get_le24(pdu->llctrl.cis_rsp.cis_offset_min); + uint32_t cis_offset_max = sys_get_le24(pdu->llctrl.cis_rsp.cis_offset_max); + + /* TODO: Fail procedure if offsets are invalid? */ + if (cis_offset_min <= cis_offset_max && + cis_offset_min >= ctx->data.cis_create.cis_offset_min && + cis_offset_min <= ctx->data.cis_create.cis_offset_max && + cis_offset_max <= ctx->data.cis_create.cis_offset_max && + cis_offset_max >= ctx->data.cis_create.cis_offset_min) { + /* Offsets are valid */ + ctx->data.cis_create.cis_offset_min = cis_offset_min; + ctx->data.cis_create.cis_offset_max = cis_offset_max; + } + + ctx->data.cis_create.conn_event_count = + sys_le16_to_cpu(pdu->llctrl.cis_rsp.conn_event_count); +} + +void llcp_pdu_encode_cis_ind(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + struct pdu_data_llctrl_cis_ind *p; + + 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; + + p = &pdu->llctrl.cis_ind; + + p->aa[0] = ctx->data.cis_create.aa[0]; + p->aa[1] = ctx->data.cis_create.aa[1]; + p->aa[2] = ctx->data.cis_create.aa[2]; + p->aa[3] = ctx->data.cis_create.aa[3]; + + sys_put_le24(ctx->data.cis_create.cis_offset_min, p->cis_offset); + sys_put_le24(ctx->data.cis_create.cig_sync_delay, p->cig_sync_delay); + sys_put_le24(ctx->data.cis_create.cis_sync_delay, p->cis_sync_delay); + + p->conn_event_count = sys_cpu_to_le16(ctx->data.cis_create.conn_event_count); +} +#endif /* CONFIG_BT_CTLR_CENTRAL_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) {