diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index 334fbd54768..d1cbef42f59 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -172,6 +172,13 @@ config BT_PER_ADV_SYNC_TRANSFER_RECEIVER config BT_PER_ADV_SYNC_TRANSFER_SENDER bool "Periodic Advertising Sync Transfer sender" depends on !BT_CTLR || BT_CTLR_SYNC_TRANSFER_SENDER_SUPPORT + +config BT_SCA_UPDATE + bool "Sleep Clock Accuracy Update" + default y if BT_ISO_PERIPHERAL || BT_ISO_CENTRAL + depends on !BT_CTLR || BT_CTLR_SCA_UPDATE_SUPPORT + help + Enable support for Bluetooth 5.1 Sleep Clock Accuracy Update Procedure endif # BT_CONN rsource "Kconfig.iso" diff --git a/subsys/bluetooth/controller/Kconfig b/subsys/bluetooth/controller/Kconfig index a4888748d7a..4e1781361eb 100644 --- a/subsys/bluetooth/controller/Kconfig +++ b/subsys/bluetooth/controller/Kconfig @@ -78,6 +78,9 @@ config BT_CTLR_MIN_USED_CHAN_SUPPORT config BT_CTLR_SMI_SUPPORT bool +config BT_CTLR_SCA_UPDATE_SUPPORT + bool + config BT_CTLR_CONN_RSSI_SUPPORT bool @@ -450,6 +453,14 @@ config BT_CTLR_MIN_USED_CHAN Enable support for Bluetooth 5.0 Minimum Number of Used Channels Procedure in the Controller. +config BT_CTLR_SCA_UPDATE + bool "Sleep Clock Accuracy Update procedure" + depends on BT_SCA_UPDATE && BT_CTLR_SCA_UPDATE_SUPPORT + default y if BT_CTLR_CONN_ISO + help + Enable support for Bluetooth 5.1 Sleep Clock Accuracy + Update procedure in the Controller. + config BT_CTLR_CONN_RSSI bool "Connection RSSI" depends on BT_CTLR_CONN_RSSI_SUPPORT diff --git a/subsys/bluetooth/controller/Kconfig.ll_sw_split b/subsys/bluetooth/controller/Kconfig.ll_sw_split index c2d3753c109..6ff0ba73e5e 100644 --- a/subsys/bluetooth/controller/Kconfig.ll_sw_split +++ b/subsys/bluetooth/controller/Kconfig.ll_sw_split @@ -35,6 +35,7 @@ config BT_LLL_VENDOR_NORDIC select BT_CTLR_CTEINLINE_SUPPORT if HAS_HW_NRF_RADIO_DFE select BT_CTLR_CHAN_SEL_2_SUPPORT select BT_CTLR_MIN_USED_CHAN_SUPPORT + select BT_CTLR_SCA_UPDATE_SUPPORT select BT_CTLR_DTM_HCI_SUPPORT select BT_CTLR_CONN_RSSI_SUPPORT diff --git a/subsys/bluetooth/controller/include/ll_feat.h b/subsys/bluetooth/controller/include/ll_feat.h index c32b637d4a6..ec87686f45f 100644 --- a/subsys/bluetooth/controller/include/ll_feat.h +++ b/subsys/bluetooth/controller/include/ll_feat.h @@ -171,6 +171,12 @@ #define LL_FEAT_BIT_RX_CTE 0 #endif /* !CONFIG_BT_CTLR_DF && !CONFIG_BT_CTLR_DF_CTE_RX */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) +#define LL_FEAT_BIT_SCA_UPDATE BIT64(BT_LE_FEAT_BIT_SCA_UPDATE) +#else /* !CONFIG_BT_CTLR_SCA_UPDATE */ +#define LL_FEAT_BIT_SCA_UPDATE 0 +#endif /* !CONFIG_BT_CTLR_SCA_UPDATE */ + #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) #define LL_FEAT_BIT_CIS_CENTRAL BIT64(BT_LE_FEAT_BIT_CIS_CENTRAL) #else /* !CONFIG_BT_CTLR_CENTRAL_ISO */ @@ -252,6 +258,7 @@ LL_FEAT_BIT_ANT_SWITCH_TX_AOD | \ LL_FEAT_BIT_ANT_SWITCH_RX_AOA | \ LL_FEAT_BIT_RX_CTE | \ + LL_FEAT_BIT_SCA_UPDATE | \ LL_FEAT_BIT_CHAN_SEL_2 | \ LL_FEAT_BIT_MIN_USED_CHAN | \ LL_FEAT_BIT_CIS_CENTRAL | \ diff --git a/subsys/bluetooth/controller/ll_sw/lll.h b/subsys/bluetooth/controller/ll_sw/lll.h index ae087b21d31..423da586dd6 100644 --- a/subsys/bluetooth/controller/ll_sw/lll.h +++ b/subsys/bluetooth/controller/ll_sw/lll.h @@ -308,6 +308,7 @@ enum node_rx_type { NODE_RX_TYPE_SCAN_INDICATION, NODE_RX_TYPE_CIS_REQUEST, NODE_RX_TYPE_CIS_ESTABLISHED, + NODE_RX_TYPE_REQ_PEER_SCA_COMPLETE, NODE_RX_TYPE_MESH_ADV_CPLT, NODE_RX_TYPE_MESH_REPORT, NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT, diff --git a/subsys/bluetooth/controller/ll_sw/pdu.h b/subsys/bluetooth/controller/ll_sw/pdu.h index 24f143c3965..5f19dcb71fc 100644 --- a/subsys/bluetooth/controller/ll_sw/pdu.h +++ b/subsys/bluetooth/controller/ll_sw/pdu.h @@ -524,6 +524,8 @@ enum pdu_data_llctrl_type { PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND = 0x19, PDU_DATA_LLCTRL_TYPE_CTE_REQ = 0x1A, PDU_DATA_LLCTRL_TYPE_CTE_RSP = 0x1B, + PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ = 0x1D, + PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP = 0x1E, PDU_DATA_LLCTRL_TYPE_CIS_REQ = 0x1F, PDU_DATA_LLCTRL_TYPE_CIS_RSP = 0x20, PDU_DATA_LLCTRL_TYPE_CIS_IND = 0x21, @@ -697,6 +699,14 @@ struct pdu_data_llctrl_cte_rsp { /* no members */ } __packed; +struct pdu_data_llctrl_clock_accuracy_req { + uint8_t sca; +} __packed; + +struct pdu_data_llctrl_clock_accuracy_rsp { + uint8_t sca; +} __packed; + struct pdu_data_llctrl_cis_req { uint8_t cig_id; uint8_t cis_id; @@ -787,6 +797,8 @@ struct pdu_data_llctrl { struct pdu_data_llctrl_min_used_chans_ind min_used_chans_ind; struct pdu_data_llctrl_cte_req cte_req; struct pdu_data_llctrl_cte_rsp cte_rsp; + struct pdu_data_llctrl_clock_accuracy_req sca_req; + struct pdu_data_llctrl_clock_accuracy_rsp sca_rsp; struct pdu_data_llctrl_cis_req cis_req; struct pdu_data_llctrl_cis_rsp cis_rsp; struct pdu_data_llctrl_cis_ind cis_ind; diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h index 331186c1d65..e97c0917683 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h @@ -598,3 +598,8 @@ struct node_rx_pu { uint8_t tx; uint8_t rx; }; + +struct node_rx_sca { + uint8_t status; + uint8_t sca; +}; diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.c b/subsys/bluetooth/controller/ll_sw/ull_llcp.c index ef025876704..c57b0c7b19d 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.c @@ -24,6 +24,7 @@ #include "ll_settings.h" #include "lll.h" +#include "lll_clock.h" #include "lll/lll_df_types.h" #include "lll_conn.h" #include "lll_conn_iso.h" @@ -370,6 +371,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_SCA_UPDATE) + case PROC_SCA_UPDATE: + llcp_lp_comm_init_proc(ctx); + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -451,6 +457,11 @@ struct proc_ctx *llcp_create_remote_procedure(enum llcp_proc proc) llcp_rp_comm_init_proc(ctx); break; #endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + llcp_rp_comm_init_proc(ctx); + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ @@ -910,6 +921,27 @@ uint8_t ull_cp_data_length_update(struct ll_conn *conn, uint16_t max_tx_octets, } #endif /* CONFIG_BT_CTLR_DATA_LENGTH */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) +uint8_t ull_cp_req_peer_sca(struct ll_conn *conn) +{ + struct proc_ctx *ctx; + + if (!feature_sca(conn)) { + return BT_HCI_ERR_UNSUPP_REMOTE_FEATURE; + } + + ctx = llcp_create_local_procedure(PROC_SCA_UPDATE); + + if (!ctx) { + return BT_HCI_ERR_CMD_DISALLOWED; + } + + llcp_lr_enqueue(conn, ctx); + + return BT_HCI_ERR_SUCCESS; +} +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ + #if defined(CONFIG_BT_CTLR_LE_ENC) uint8_t ull_cp_ltk_req_reply(struct ll_conn *conn, const uint8_t ltk[16]) { @@ -1462,6 +1494,26 @@ static bool pdu_validate_cte_resp(struct pdu_data *pdu) } #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) +static bool pdu_validate_clock_accuracy_req(struct pdu_data *pdu) +{ + if (pdu->len != sizeof(pdu->llctrl.sca_req) + 1) { + return false; + } + + return true; +} +#endif /* CONFIG_BT_CTLR_DATA_LENGTH */ + +static bool pdu_validate_clock_accuracy_rsp(struct pdu_data *pdu) +{ + if (pdu->len != sizeof(pdu->llctrl.sca_rsp) + 1) { + return false; + } + + return true; +} + typedef bool (*pdu_param_validate_t)(struct pdu_data *pdu); struct pdu_validate { @@ -1530,6 +1582,10 @@ static const struct pdu_validate pdu_validate[] = { #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP) [PDU_DATA_LLCTRL_TYPE_CTE_RSP] = { pdu_validate_cte_resp }, #endif /* PDU_DATA_LLCTRL_TYPE_CTE_RSP */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + [PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ] = { pdu_validate_clock_accuracy_req }, +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ + [PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP] = { pdu_validate_clock_accuracy_rsp }, }; static bool pdu_is_valid(struct pdu_data *pdu) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.h b/subsys/bluetooth/controller/ll_sw/ull_llcp.h index c3edb216a8f..698febec4e9 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.h @@ -216,3 +216,10 @@ void ull_cp_cte_req_set_disable(struct ll_conn *conn); */ void ull_cp_cte_rsp_enable(struct ll_conn *conn, bool enable, uint8_t max_cte_len, uint8_t cte_types); + +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) +/** + * @brief Initiate a Sleep Clock Accuracy Update Procedure. + */ +uint8_t ull_cp_req_peer_sca(struct ll_conn *conn); +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c index fd05c214038..8030d2c53f0 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c @@ -196,6 +196,12 @@ static void lp_comm_tx(struct ll_conn *conn, struct proc_ctx *ctx) ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CTE_RSP; break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + llcp_pdu_encode_clock_accuracy_req(ctx, pdu); + ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP; + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -351,6 +357,29 @@ static void lp_comm_complete_cte_req(struct ll_conn *conn, struct proc_ctx *ctx) } #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) +static void lp_sca_ntf(struct ll_conn *conn, struct proc_ctx *ctx) +{ + struct node_rx_pdu *ntf; + struct node_rx_sca *pdu; + + /* Allocate ntf node */ + ntf = llcp_ntf_alloc(); + LL_ASSERT(ntf); + + ntf->hdr.type = NODE_RX_TYPE_REQ_PEER_SCA_COMPLETE; + ntf->hdr.handle = conn->lll.handle; + pdu = (struct node_rx_sca *)ntf->pdu; + + pdu->status = ctx->data.sca_update.error_code; + pdu->sca = ctx->data.sca_update.sca; + + /* Enqueue notification towards LL */ + ll_rx_put(ntf->hdr.link, ntf); + ll_rx_sched(); +} +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ + static void lp_comm_ntf(struct ll_conn *conn, struct proc_ctx *ctx) { struct node_rx_pdu *ntf; @@ -506,6 +535,25 @@ static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t lp_comm_complete_cte_req(conn, ctx); break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + switch (ctx->response_opcode) { + case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP: + /* Peer does not support SCA update, so disable on current connection */ + feature_unmask_features(conn, LL_FEAT_BIT_SCA_UPDATE); + ctx->data.sca_update.error_code = BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL; + /* Fall through to complete procedure */ + case PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP: + lp_sca_ntf(conn, ctx); + llcp_lr_complete(conn); + ctx->state = LP_COMMON_STATE_IDLE; + break; + default: + /* Illegal response opcode */ + lp_comm_terminate_invalid_pdu(conn, ctx); + } + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -642,6 +690,16 @@ static void lp_comm_send_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t } break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + if (llcp_lr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { + ctx->state = LP_COMMON_STATE_WAIT_TX; + } else { + lp_comm_tx(conn, ctx); + ctx->state = LP_COMMON_STATE_WAIT_RX; + } + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -766,6 +824,11 @@ static void lp_comm_rx_decode(struct ll_conn *conn, struct proc_ctx *ctx, struct llcp_pdu_decode_cte_rsp(ctx, pdu); break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP: + llcp_pdu_decode_clock_accuracy_rsp(ctx, pdu); + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND: llcp_pdu_decode_reject_ext_ind(ctx, pdu); break; @@ -955,6 +1018,11 @@ static void rp_comm_rx_decode(struct ll_conn *conn, struct proc_ctx *ctx, struct llcp_pdu_decode_cte_req(ctx, pdu); break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ: + llcp_pdu_decode_clock_accuracy_req(ctx, pdu); + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown opcode */ LL_ASSERT(0); @@ -1029,6 +1097,13 @@ static void rp_comm_tx(struct ll_conn *conn, struct proc_ctx *ctx) break; } #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + llcp_pdu_encode_clock_accuracy_rsp(ctx, pdu); + ctx->tx_ack = tx; + ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_UNUSED; + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -1217,6 +1292,19 @@ static void rp_comm_send_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t } break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + /* Always respond to remote SCA */ + if (llcp_rr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { + ctx->state = RP_COMMON_STATE_WAIT_TX; + } else { + rp_comm_tx(conn, ctx); + + /* Wait for the peer to have ack'ed the RSP before completing */ + ctx->state = RP_COMMON_STATE_WAIT_TX_ACK; + } + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -1303,6 +1391,13 @@ static void rp_comm_st_wait_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, u ctx->state = RP_COMMON_STATE_IDLE; } #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: { + ctx->tx_ack = NULL; + llcp_rr_complete(conn); + ctx->state = RP_COMMON_STATE_IDLE; + } +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Ignore other procedures */ break; diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h index c27063807f5..b2db551a045 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h @@ -120,7 +120,16 @@ static inline bool feature_phy_coded(struct ll_conn *conn) static inline bool feature_cte_req(struct ll_conn *conn) { #if defined(CONFIG_BT_CTLR_DF) && defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ) - return conn->llcp.fex.features_used & LL_FEAT_BIT_CONNECTION_CTE_REQ; + return (conn->llcp.fex.features_used & LL_FEAT_BIT_CONNECTION_CTE_REQ) != 0; +#else + return 0; +#endif +} + +static inline bool feature_sca(struct ll_conn *conn) +{ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + return (conn->llcp.fex.features_used & LL_FEAT_BIT_SCA_UPDATE) != 0; #else return 0; #endif @@ -162,7 +171,6 @@ static inline bool feature_peer_smi_tx(struct ll_conn *conn) * tone_ext * per_adv_sync_tx * per_adv_sync_rx - * sleep_upd * rpk_valid * iso_central * iso_periph diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h index 1ec7c70432e..3de36405ce2 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h @@ -28,6 +28,7 @@ enum llcp_proc { PROC_CTE_REQ, PROC_CIS_CREATE, PROC_CIS_TERMINATE, + PROC_SCA_UPDATE, /* A helper enum entry, to use in pause procedure context */ PROC_NONE = 0x0, }; @@ -295,6 +296,12 @@ struct proc_ctx { uint8_t cis_id; uint8_t error_code; } cis_term; +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + struct { + uint8_t sca; + uint8_t error_code; + } sca_update; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ } data; struct { @@ -670,6 +677,17 @@ void llcp_ntf_encode_length_change(struct ll_conn *conn, #endif /* CONFIG_BT_CTLR_DATA_LENGTH */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) +/* + * Sleep Clock Accuracy Update Procedure Helper + */ +void llcp_pdu_encode_clock_accuracy_req(struct proc_ctx *ctx, struct pdu_data *pdu); +void llcp_pdu_encode_clock_accuracy_rsp(struct proc_ctx *ctx, struct pdu_data *pdu); +void llcp_pdu_decode_clock_accuracy_req(struct proc_ctx *ctx, struct pdu_data *pdu); +void llcp_pdu_decode_clock_accuracy_rsp(struct proc_ctx *ctx, struct pdu_data *pdu); + +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ + #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ) /* * Constant Tone Request Procedure Helper diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c index 9fef3eec22a..c3a8847a4f3 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_SCA_UPDATE) + case PROC_SCA_UPDATE: + llcp_lp_comm_rx(conn, ctx, rx); + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -321,6 +326,11 @@ static void lr_act_run(struct ll_conn *conn) llcp_lp_comm_run(conn, ctx, NULL); break; #endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + llcp_lp_comm_run(conn, ctx, NULL); + break; +#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ default: /* Unknown procedure */ LL_ASSERT(0); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c index 580267768a1..f5fc3d723ae 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c @@ -23,6 +23,7 @@ #include "ll_settings.h" #include "lll.h" +#include "lll_clock.h" #include "lll/lll_df_types.h" #include "lll_conn.h" #include "lll_conn_iso.h" @@ -904,3 +905,46 @@ void llcp_pdu_decode_cis_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pd 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) */ + +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) +/* + * SCA Update Procedure Helpers + */ +void llcp_pdu_encode_clock_accuracy_req(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + struct pdu_data_llctrl_clock_accuracy_req *p = &pdu->llctrl.sca_req; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = offsetof(struct pdu_data_llctrl, sca_req) + + sizeof(struct pdu_data_llctrl_clock_accuracy_req); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ; + /* Currently we do not support variable SCA, so we always 'report' current SCA */ + p->sca = lll_clock_sca_local_get(); +} + +void llcp_pdu_encode_clock_accuracy_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + struct pdu_data_llctrl_clock_accuracy_rsp *p = &pdu->llctrl.sca_rsp; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = offsetof(struct pdu_data_llctrl, sca_rsp) + + sizeof(struct pdu_data_llctrl_clock_accuracy_rsp); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP; + /* Currently we do not support variable SCA, so we always 'report' current SCA */ + p->sca = lll_clock_sca_local_get(); +} + +void llcp_pdu_decode_clock_accuracy_req(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + struct pdu_data_llctrl_clock_accuracy_req *p = &pdu->llctrl.sca_req; + + ctx->data.sca_update.sca = p->sca; +} + +void llcp_pdu_decode_clock_accuracy_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + struct pdu_data_llctrl_clock_accuracy_rsp *p = &pdu->llctrl.sca_rsp; + + ctx->data.sca_update.sca = p->sca; +} +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c index daa10a3916e..0c8518685ec 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c @@ -94,6 +94,7 @@ static bool proc_with_instant(struct proc_ctx *ctx) case PROC_CTE_REQ: case PROC_CIS_TERMINATE: case PROC_CIS_CREATE: + case PROC_SCA_UPDATE: return 0U; case PROC_PHY_UPDATE: case PROC_CONN_UPDATE: @@ -278,6 +279,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_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + llcp_rp_comm_rx(conn, ctx, rx); + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -303,6 +309,11 @@ void llcp_rr_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx * case PROC_CTE_REQ: llcp_rp_comm_tx_ack(conn, ctx, tx); break; +#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + llcp_rp_comm_tx_ack(conn, ctx, tx); + break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ default: /* Ignore tx_ack */ @@ -399,6 +410,11 @@ static void rr_act_run(struct ll_conn *conn) llcp_rp_comm_run(conn, ctx, NULL); break; #endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + case PROC_SCA_UPDATE: + llcp_rp_comm_run(conn, ctx, NULL); + break; +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -831,6 +847,9 @@ static const struct proc_role new_proc_lut[] = { #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 */ +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) + [PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ] = { PROC_SCA_UPDATE, ACCEPT_ROLE_BOTH }, +#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ }; void llcp_rr_new(struct ll_conn *conn, struct node_rx_pdu *rx, bool valid_pdu) diff --git a/tests/bluetooth/controller/common/include/helper_features.h b/tests/bluetooth/controller/common/include/helper_features.h index 534931eca3a..ff76584f2fb 100644 --- a/tests/bluetooth/controller/common/include/helper_features.h +++ b/tests/bluetooth/controller/common/include/helper_features.h @@ -165,7 +165,7 @@ #define FEAT_PER_ADV_SYNC_RX 0x00 #endif -#if defined(CONFIG_MISSING) +#if defined(CONFIG_BT_CTLR_SCA_UPDATE) #define FEAT_SLEEP_UPD 0x4000000 #else #define FEAT_SLEEP_UPD 0x00 diff --git a/tests/bluetooth/controller/common/include/helper_pdu.h b/tests/bluetooth/controller/common/include/helper_pdu.h index 4131b4d25a5..a43912cb2b7 100644 --- a/tests/bluetooth/controller/common/include/helper_pdu.h +++ b/tests/bluetooth/controller/common/include/helper_pdu.h @@ -59,6 +59,9 @@ 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_encode_sca_req(struct pdu_data *pdu, void *param); +void helper_pdu_encode_sca_rsp(struct pdu_data *pdu, void *param); + void helper_pdu_verify_ping_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param); void helper_pdu_verify_ping_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param); @@ -149,6 +152,12 @@ void helper_pdu_verify_cis_ind(const char *file, uint32_t line, struct pdu_data void helper_pdu_verify_cis_terminate_ind(const char *file, uint32_t line, struct pdu_data *pdu, void *param); +void helper_pdu_verify_sca_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param); +void helper_pdu_verify_sca_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param); + +void helper_node_verify_peer_sca_update(const char *file, uint32_t line, struct node_rx_pdu *rx, + void *param); + enum helper_pdu_opcode { LL_VERSION_IND, LL_LE_PING_REQ, @@ -178,6 +187,8 @@ enum helper_pdu_opcode { LL_LENGTH_RSP, LL_CTE_REQ, LL_CTE_RSP, + LL_CLOCK_ACCURACY_REQ, + LL_CLOCK_ACCURACY_RSP, LL_CIS_REQ, LL_CIS_RSP, LL_CIS_IND, @@ -192,7 +203,7 @@ enum helper_node_opcode { NODE_CTE_RSP, NODE_CIS_REQUEST, NODE_CIS_ESTABLISHED, - + NODE_PEER_SCA_UPDATE, }; 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 0cc2de21f55..f4477226168 100644 --- a/tests/bluetooth/controller/common/src/helper_pdu.c +++ b/tests/bluetooth/controller/common/src/helper_pdu.c @@ -480,6 +480,28 @@ void helper_pdu_encode_cis_terminate_ind(struct pdu_data *pdu, void *param) pdu->llctrl.cis_terminate_ind.error_code = p->error_code; } +void helper_pdu_encode_sca_req(struct pdu_data *pdu, void *param) +{ + struct pdu_data_llctrl_clock_accuracy_req *p = param; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = offsetof(struct pdu_data_llctrl, sca_req) + + sizeof(struct pdu_data_llctrl_clock_accuracy_req); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ; + pdu->llctrl.sca_req.sca = p->sca; +} + +void helper_pdu_encode_sca_rsp(struct pdu_data *pdu, void *param) +{ + struct pdu_data_llctrl_clock_accuracy_rsp *p = param; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = offsetof(struct pdu_data_llctrl, sca_rsp) + + sizeof(struct pdu_data_llctrl_clock_accuracy_rsp); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP; + pdu->llctrl.sca_rsp.sca = p->sca; +} + void helper_pdu_verify_version_ind(const char *file, uint32_t line, struct pdu_data *pdu, void *param) { @@ -763,6 +785,18 @@ void helper_node_verify_phy_update(const char *file, uint32_t line, struct node_ zassert_equal(pdu->status, p->status, "Status mismatch.\nCalled at %s:%d\n", file, line); } +void helper_node_verify_peer_sca_update(const char *file, uint32_t line, struct node_rx_pdu *rx, + void *param) +{ + struct node_rx_sca *pdu = (struct node_rx_sca *)rx->pdu; + struct node_rx_sca *p = param; + + zassert_equal(rx->hdr.type, NODE_RX_TYPE_REQ_PEER_SCA_COMPLETE, + "Not an SCA node.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->status, p->status, "Status mismatch.\nCalled at %s:%d\n", file, line); + zassert_equal(pdu->sca, p->sca, "SCA mismatch.\nCalled at %s:%d\n", file, line); +} + void helper_pdu_verify_unknown_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param) { @@ -1201,3 +1235,19 @@ void helper_pdu_verify_cis_terminate_ind(const char *file, uint32_t line, struct zassert_equal(pdu->llctrl.cis_terminate_ind.error_code, p->error_code, "Error code mismatch.\nCalled at %s:%d\n", file, line); } + +void helper_pdu_verify_sca_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param) +{ + zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file, + line); + zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ, + "Not a LL_CLOCK_ACCURACY_REQ. Called at %s:%d\n", file, line); +} + +void helper_pdu_verify_sca_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param) +{ + zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file, + line); + zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP, + "Not a LL_CLOCK_ACCURACY_RSP.\nCalled at %s:%d\n", file, line); +} diff --git a/tests/bluetooth/controller/common/src/helper_util.c b/tests/bluetooth/controller/common/src/helper_util.c index 6f3daa6d0aa..49841674578 100644 --- a/tests/bluetooth/controller/common/src/helper_util.c +++ b/tests/bluetooth/controller/common/src/helper_util.c @@ -87,6 +87,8 @@ 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_CLOCK_ACCURACY_REQ] = helper_pdu_encode_sca_req, + [LL_CLOCK_ACCURACY_RSP] = helper_pdu_encode_sca_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, @@ -123,6 +125,8 @@ 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_CLOCK_ACCURACY_REQ] = helper_pdu_verify_sca_req, + [LL_CLOCK_ACCURACY_RSP] = helper_pdu_verify_sca_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, @@ -157,6 +161,8 @@ 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_CLOCK_ACCURACY_REQ] = NULL, + [LL_CLOCK_ACCURACY_RSP] = NULL, [LL_CIS_REQ] = NULL, [LL_CIS_RSP] = NULL, [LL_CIS_IND] = NULL, @@ -188,6 +194,8 @@ 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_CLOCK_ACCURACY_REQ] = NULL, + [LL_CLOCK_ACCURACY_RSP] = NULL, [LL_CIS_REQ] = NULL, [LL_CIS_RSP] = NULL, [LL_CIS_IND] = NULL, @@ -201,6 +209,7 @@ helper_node_verify_func_t *const helper_node_verify[] = { [NODE_CTE_RSP] = helper_node_verify_cte_rsp, [NODE_CIS_REQUEST] = helper_node_verify_cis_request, [NODE_CIS_ESTABLISHED] = helper_node_verify_cis_established, + [NODE_PEER_SCA_UPDATE] = helper_node_verify_peer_sca_update, }; /* diff --git a/tests/bluetooth/controller/ctrl_sca_update/CMakeLists.txt b/tests/bluetooth/controller/ctrl_sca_update/CMakeLists.txt new file mode 100644 index 00000000000..90d3c55b8ce --- /dev/null +++ b/tests/bluetooth/controller/ctrl_sca_update/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +FILE(GLOB SOURCES + src/*.c +) + +project(bluetooth_ull_llcp_sca_update) +find_package(Zephyr COMPONENTS unittest 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_sca_update/src/main.c b/tests/bluetooth/controller/ctrl_sca_update/src/main.c new file mode 100644 index 00000000000..ba120e72e1c --- /dev/null +++ b/tests/bluetooth/controller/ctrl_sca_update/src/main.c @@ -0,0 +1,568 @@ +/* + * 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_feat.h" +#include "ll_settings.h" + +#include "lll.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_iso_types.h" +#include "ull_conn_types.h" +#include "ull_llcp.h" +#include "ull_conn_internal.h" +#include "ull_llcp_internal.h" +#include "ull_llcp_features.h" + +#include "helper_pdu.h" +#include "helper_util.h" + +struct ll_conn conn; + +static void setup(void) +{ + test_setup(&conn); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | SCA Update Proc. | | + * |--------------------------->| | + * | | | + * | | LL_CLOCK_ACCURACY_REQ | + * | |------------------------------->| + * | | | + * | | LL_CLOCK_ACCURACY_RSP | + * | |<-------------------------------| + * | | | + * | Start | | + * | SCA UPdate Proc. | | + * |--------------------------->| | + * | | | + * | | LL_CLOCK_ACCURACY_REQ | + * | |------------------------------->| + * | | | + * | | LL_UNKNOWN_RSP | + * | |<-------------------------------| + * | | | + */ +void test_sca_central_loc(void) +{ + uint8_t err; + struct node_tx *tx; + struct node_rx_pdu *ntf; + struct node_rx_sca scau = { .status = BT_HCI_ERR_SUCCESS, .sca = 2 }; + struct pdu_data_llctrl_clock_accuracy_req local_sca_req = { }; + struct pdu_data_llctrl_clock_accuracy_rsp remote_sca_rsp = { .sca = 2 }; + struct pdu_data_llctrl_unknown_rsp unknown_rsp = { + .type = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate an SCA Procedure */ + err = ull_cp_req_peer_sca(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Confirm SCA Update is indicated as supported */ + zassert_equal(feature_sca(&conn), true, "SCA Update Feature masked out"); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CLOCK_ACCURACY_REQ, &conn, &tx, &local_sca_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_CLOCK_ACCURACY_RSP, &conn, &remote_sca_rsp); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination not 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, 0, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should be one notification due to Peer SCA Request */ + ut_rx_node(NODE_PEER_SCA_UPDATE, &ntf, &scau); + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + /* Initiate another SCA Procedure */ + err = ull_cp_req_peer_sca(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CLOCK_ACCURACY_REQ, &conn, &tx, &local_sca_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_UNKNOWN_RSP, &conn, &unknown_rsp); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Confirm SCA Update is now indicated as NOT supported */ + zassert_equal(feature_sca(&conn), false, "SCA Update Feature masked in"); + + /* Termination not 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, 0, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + scau.status = BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL; + scau.sca = 0; + + /* There should be one notification due to Peer SCA Request */ + ut_rx_node(NODE_PEER_SCA_UPDATE, &ntf, &scau); + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | SCA Update Proc. | | + * |--------------------------->| | + * | | | + * | | LL_CLOCK_ACCURACY_REQ | + * | |------------------------->| + * | | | + * | | LL__RSP | + * | |<-------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~ + * | | | + */ +void test_sca_central_loc_invalid_rsp(void) +{ + uint8_t err; + struct node_tx *tx; + + struct pdu_data_llctrl_reject_ind reject_ind = { + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ, + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_clock_accuracy_req local_sca_req = {}; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate an SCA Procedure */ + err = ull_cp_req_peer_sca(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CLOCK_ACCURACY_REQ, &conn, &tx, &local_sca_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + /* Initiate another SCA Procedure */ + err = ull_cp_req_peer_sca(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CLOCK_ACCURACY_REQ, &conn, &tx, &local_sca_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | SCA Update Proc. | | + * |--------------------------->| | + * | | | + * | | LL_CLOCK_ACCURACY_REQ | + * | |------------------------->| + * | | | + * | | LL__RSP | + * | |<-------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~ + * | | | + */ +void test_sca_peripheral_loc_invalid_rsp(void) +{ + uint8_t err; + struct node_tx *tx; + + struct pdu_data_llctrl_reject_ind reject_ind = { + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ, + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_clock_accuracy_req local_sca_req = {}; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate an SCA Procedure */ + err = ull_cp_req_peer_sca(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CLOCK_ACCURACY_REQ, &conn, &tx, &local_sca_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + /* Initiate another SCA Procedure */ + err = ull_cp_req_peer_sca(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CLOCK_ACCURACY_REQ, &conn, &tx, &local_sca_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | SCA Update Proc. | | + * |--------------------------->| | + * | | | + * | | LL_CLOCK_ACCURACY_REQ | + * | |------------------------->| + * | | | + * | | LL_CLOCK_ACCURACY_RSP | + * | |<-------------------------| + * | | | + * | | | + */ +void test_ping_periph_loc(void) +{ + uint8_t err; + struct node_tx *tx; + struct node_rx_pdu *ntf; + struct node_rx_sca scau = { .status = BT_HCI_ERR_SUCCESS, .sca = 2 }; + struct pdu_data_llctrl_clock_accuracy_req local_sca_req = { }; + struct pdu_data_llctrl_clock_accuracy_rsp remote_sca_rsp = { .sca = 2 }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate an SCA Procedure */ + err = ull_cp_req_peer_sca(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CLOCK_ACCURACY_REQ, &conn, &tx, &local_sca_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_CLOCK_ACCURACY_RSP, &conn, &remote_sca_rsp); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination not 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, 0, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should be one notification due to Peer SCA Request */ + ut_rx_node(NODE_PEER_SCA_UPDATE, &ntf, &scau); + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CLOCK_ACCURACY_REQ | + * | |------------------------->| + * | | | + * | | LL_CLOCK_ACCURACY_RSP | + * | |<-------------------------| + * | | | + * | | | + */ +void test_ping_central_rem(void) +{ + struct node_tx *tx; + + struct pdu_data_llctrl_clock_accuracy_req local_sca_req = { }; + + struct pdu_data_llctrl_clock_accuracy_rsp remote_sca_rsp = { }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Tx */ + lt_tx(LL_CLOCK_ACCURACY_REQ, &conn, &local_sca_req); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CLOCK_ACCURACY_RSP, &conn, &tx, &remote_sca_rsp); + lt_rx_q_is_empty(&conn); + + event_tx_ack(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CLOCK_ACCURACY_REQ | + * | |------------------------->| + * | | | + * | | LL_CLOCK_ACCURACY_RSP | + * | |<-------------------------| + * | | | + * | | | + */ +void test_ping_periph_rem(void) +{ + struct node_tx *tx; + + struct pdu_data_llctrl_clock_accuracy_req local_sca_req = { }; + + struct pdu_data_llctrl_clock_accuracy_rsp remote_sca_rsp = { }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Tx */ + lt_tx(LL_CLOCK_ACCURACY_REQ, &conn, &local_sca_req); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CLOCK_ACCURACY_RSP, &conn, &tx, &remote_sca_rsp); + lt_rx_q_is_empty(&conn); + + event_tx_ack(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + +void test_main(void) +{ + ztest_test_suite(sca, + ztest_unit_test_setup_teardown(test_sca_central_loc, setup, + unit_test_noop), + ztest_unit_test_setup_teardown(test_sca_central_loc_invalid_rsp, setup, + unit_test_noop), + ztest_unit_test_setup_teardown(test_ping_periph_loc, setup, + unit_test_noop), + ztest_unit_test_setup_teardown(test_sca_peripheral_loc_invalid_rsp, setup, + unit_test_noop), + ztest_unit_test_setup_teardown(test_ping_central_rem, setup, + unit_test_noop), + ztest_unit_test_setup_teardown(test_ping_periph_rem, setup, + unit_test_noop) + ); + + ztest_run_test_suite(sca); +} diff --git a/tests/bluetooth/controller/ctrl_sca_update/testcase.yaml b/tests/bluetooth/controller/ctrl_sca_update/testcase.yaml new file mode 100644 index 00000000000..e0cb909a58d --- /dev/null +++ b/tests/bluetooth/controller/ctrl_sca_update/testcase.yaml @@ -0,0 +1,5 @@ +common: + tags: test_framework bluetooth bt_sca_update bt_ull_llcp +tests: + bluetooth.controller.ctrl_sca_update.test: + type: unit diff --git a/tests/bluetooth/controller/mock_ctrl/include/kconfig.h b/tests/bluetooth/controller/mock_ctrl/include/kconfig.h index 403df1bc1e2..6a7758345b9 100644 --- a/tests/bluetooth/controller/mock_ctrl/include/kconfig.h +++ b/tests/bluetooth/controller/mock_ctrl/include/kconfig.h @@ -162,6 +162,10 @@ #define CONFIG_BT_CTLR_DF_CTE_RX y #endif +#ifndef CONFIG_BT_CTLR_SCA_UPDATE +#define CONFIG_BT_CTLR_SCA_UPDATE y +#endif + #ifndef CONFIG_BT_CTLR_DF_MAX_ANT_SW_PATTERN_LEN #define CONFIG_BT_CTLR_DF_MAX_ANT_SW_PATTERN_LEN 38 #endif diff --git a/tests/bluetooth/controller/mock_ctrl/src/lll_clock.c b/tests/bluetooth/controller/mock_ctrl/src/lll_clock.c index e1c11b02f7a..9d5f58853ca 100644 --- a/tests/bluetooth/controller/mock_ctrl/src/lll_clock.c +++ b/tests/bluetooth/controller/mock_ctrl/src/lll_clock.c @@ -41,6 +41,11 @@ int lll_hfclock_off(void) return 0; } +uint8_t lll_clock_sca_local_get(void) +{ + return 0; +} + uint32_t lll_clock_ppm_local_get(void) { return 0;