Bluetooth: controller: implementing SCA update procedure PDU flow

Adding PDU flow and unittest for Sleep clock accuracy procedure

Signed-off-by: Erik Brockhoff <erbr@oticon.com>

wip kconf
This commit is contained in:
Erik Brockhoff 2022-10-06 11:28:14 +02:00 committed by Carles Cufí
commit b3b4b9e322
24 changed files with 969 additions and 4 deletions

View file

@ -172,6 +172,13 @@ config BT_PER_ADV_SYNC_TRANSFER_RECEIVER
config BT_PER_ADV_SYNC_TRANSFER_SENDER config BT_PER_ADV_SYNC_TRANSFER_SENDER
bool "Periodic Advertising Sync Transfer sender" bool "Periodic Advertising Sync Transfer sender"
depends on !BT_CTLR || BT_CTLR_SYNC_TRANSFER_SENDER_SUPPORT 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 endif # BT_CONN
rsource "Kconfig.iso" rsource "Kconfig.iso"

View file

@ -78,6 +78,9 @@ config BT_CTLR_MIN_USED_CHAN_SUPPORT
config BT_CTLR_SMI_SUPPORT config BT_CTLR_SMI_SUPPORT
bool bool
config BT_CTLR_SCA_UPDATE_SUPPORT
bool
config BT_CTLR_CONN_RSSI_SUPPORT config BT_CTLR_CONN_RSSI_SUPPORT
bool bool
@ -450,6 +453,14 @@ config BT_CTLR_MIN_USED_CHAN
Enable support for Bluetooth 5.0 Minimum Number of Used Channels Enable support for Bluetooth 5.0 Minimum Number of Used Channels
Procedure in the Controller. 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 config BT_CTLR_CONN_RSSI
bool "Connection RSSI" bool "Connection RSSI"
depends on BT_CTLR_CONN_RSSI_SUPPORT depends on BT_CTLR_CONN_RSSI_SUPPORT

View file

@ -35,6 +35,7 @@ config BT_LLL_VENDOR_NORDIC
select BT_CTLR_CTEINLINE_SUPPORT if HAS_HW_NRF_RADIO_DFE select BT_CTLR_CTEINLINE_SUPPORT if HAS_HW_NRF_RADIO_DFE
select BT_CTLR_CHAN_SEL_2_SUPPORT select BT_CTLR_CHAN_SEL_2_SUPPORT
select BT_CTLR_MIN_USED_CHAN_SUPPORT select BT_CTLR_MIN_USED_CHAN_SUPPORT
select BT_CTLR_SCA_UPDATE_SUPPORT
select BT_CTLR_DTM_HCI_SUPPORT select BT_CTLR_DTM_HCI_SUPPORT
select BT_CTLR_CONN_RSSI_SUPPORT select BT_CTLR_CONN_RSSI_SUPPORT

View file

@ -171,6 +171,12 @@
#define LL_FEAT_BIT_RX_CTE 0 #define LL_FEAT_BIT_RX_CTE 0
#endif /* !CONFIG_BT_CTLR_DF && !CONFIG_BT_CTLR_DF_CTE_RX */ #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) #if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
#define LL_FEAT_BIT_CIS_CENTRAL BIT64(BT_LE_FEAT_BIT_CIS_CENTRAL) #define LL_FEAT_BIT_CIS_CENTRAL BIT64(BT_LE_FEAT_BIT_CIS_CENTRAL)
#else /* !CONFIG_BT_CTLR_CENTRAL_ISO */ #else /* !CONFIG_BT_CTLR_CENTRAL_ISO */
@ -252,6 +258,7 @@
LL_FEAT_BIT_ANT_SWITCH_TX_AOD | \ LL_FEAT_BIT_ANT_SWITCH_TX_AOD | \
LL_FEAT_BIT_ANT_SWITCH_RX_AOA | \ LL_FEAT_BIT_ANT_SWITCH_RX_AOA | \
LL_FEAT_BIT_RX_CTE | \ LL_FEAT_BIT_RX_CTE | \
LL_FEAT_BIT_SCA_UPDATE | \
LL_FEAT_BIT_CHAN_SEL_2 | \ LL_FEAT_BIT_CHAN_SEL_2 | \
LL_FEAT_BIT_MIN_USED_CHAN | \ LL_FEAT_BIT_MIN_USED_CHAN | \
LL_FEAT_BIT_CIS_CENTRAL | \ LL_FEAT_BIT_CIS_CENTRAL | \

View file

@ -308,6 +308,7 @@ enum node_rx_type {
NODE_RX_TYPE_SCAN_INDICATION, NODE_RX_TYPE_SCAN_INDICATION,
NODE_RX_TYPE_CIS_REQUEST, NODE_RX_TYPE_CIS_REQUEST,
NODE_RX_TYPE_CIS_ESTABLISHED, NODE_RX_TYPE_CIS_ESTABLISHED,
NODE_RX_TYPE_REQ_PEER_SCA_COMPLETE,
NODE_RX_TYPE_MESH_ADV_CPLT, NODE_RX_TYPE_MESH_ADV_CPLT,
NODE_RX_TYPE_MESH_REPORT, NODE_RX_TYPE_MESH_REPORT,
NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT, NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT,

View file

@ -524,6 +524,8 @@ enum pdu_data_llctrl_type {
PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND = 0x19, PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND = 0x19,
PDU_DATA_LLCTRL_TYPE_CTE_REQ = 0x1A, PDU_DATA_LLCTRL_TYPE_CTE_REQ = 0x1A,
PDU_DATA_LLCTRL_TYPE_CTE_RSP = 0x1B, 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_REQ = 0x1F,
PDU_DATA_LLCTRL_TYPE_CIS_RSP = 0x20, PDU_DATA_LLCTRL_TYPE_CIS_RSP = 0x20,
PDU_DATA_LLCTRL_TYPE_CIS_IND = 0x21, PDU_DATA_LLCTRL_TYPE_CIS_IND = 0x21,
@ -697,6 +699,14 @@ struct pdu_data_llctrl_cte_rsp {
/* no members */ /* no members */
} __packed; } __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 { struct pdu_data_llctrl_cis_req {
uint8_t cig_id; uint8_t cig_id;
uint8_t cis_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_min_used_chans_ind min_used_chans_ind;
struct pdu_data_llctrl_cte_req cte_req; struct pdu_data_llctrl_cte_req cte_req;
struct pdu_data_llctrl_cte_rsp cte_rsp; 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_req cis_req;
struct pdu_data_llctrl_cis_rsp cis_rsp; struct pdu_data_llctrl_cis_rsp cis_rsp;
struct pdu_data_llctrl_cis_ind cis_ind; struct pdu_data_llctrl_cis_ind cis_ind;

View file

@ -598,3 +598,8 @@ struct node_rx_pu {
uint8_t tx; uint8_t tx;
uint8_t rx; uint8_t rx;
}; };
struct node_rx_sca {
uint8_t status;
uint8_t sca;
};

View file

@ -24,6 +24,7 @@
#include "ll_settings.h" #include "ll_settings.h"
#include "lll.h" #include "lll.h"
#include "lll_clock.h"
#include "lll/lll_df_types.h" #include "lll/lll_df_types.h"
#include "lll_conn.h" #include "lll_conn.h"
#include "lll_conn_iso.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); llcp_lp_comm_init_proc(ctx);
break; break;
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); LL_ASSERT(0);
@ -451,6 +457,11 @@ struct proc_ctx *llcp_create_remote_procedure(enum llcp_proc proc)
llcp_rp_comm_init_proc(ctx); llcp_rp_comm_init_proc(ctx);
break; break;
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ #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: default:
/* Unknown procedure */ /* 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 */ #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) #if defined(CONFIG_BT_CTLR_LE_ENC)
uint8_t ull_cp_ltk_req_reply(struct ll_conn *conn, const uint8_t ltk[16]) 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 */ #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); typedef bool (*pdu_param_validate_t)(struct pdu_data *pdu);
struct pdu_validate { struct pdu_validate {
@ -1530,6 +1582,10 @@ static const struct pdu_validate pdu_validate[] = {
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP) #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP)
[PDU_DATA_LLCTRL_TYPE_CTE_RSP] = { pdu_validate_cte_resp }, [PDU_DATA_LLCTRL_TYPE_CTE_RSP] = { pdu_validate_cte_resp },
#endif /* PDU_DATA_LLCTRL_TYPE_CTE_RSP */ #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) static bool pdu_is_valid(struct pdu_data *pdu)

View file

@ -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, void ull_cp_cte_rsp_enable(struct ll_conn *conn, bool enable, uint8_t max_cte_len,
uint8_t cte_types); 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 */

View file

@ -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; ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CTE_RSP;
break; break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); 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 */ #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) static void lp_comm_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
{ {
struct node_rx_pdu *ntf; 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); lp_comm_complete_cte_req(conn, ctx);
break; break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); LL_ASSERT(0);
@ -642,6 +690,16 @@ static void lp_comm_send_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
} }
break; break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); 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); llcp_pdu_decode_cte_rsp(ctx, pdu);
break; break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ #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: case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND:
llcp_pdu_decode_reject_ext_ind(ctx, pdu); llcp_pdu_decode_reject_ext_ind(ctx, pdu);
break; 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); llcp_pdu_decode_cte_req(ctx, pdu);
break; break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ #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: default:
/* Unknown opcode */ /* Unknown opcode */
LL_ASSERT(0); LL_ASSERT(0);
@ -1029,6 +1097,13 @@ static void rp_comm_tx(struct ll_conn *conn, struct proc_ctx *ctx)
break; break;
} }
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); LL_ASSERT(0);
@ -1217,6 +1292,19 @@ static void rp_comm_send_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
} }
break; break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); 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; ctx->state = RP_COMMON_STATE_IDLE;
} }
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ #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: default:
/* Ignore other procedures */ /* Ignore other procedures */
break; break;

View file

@ -120,7 +120,16 @@ static inline bool feature_phy_coded(struct ll_conn *conn)
static inline bool feature_cte_req(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) #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 #else
return 0; return 0;
#endif #endif
@ -162,7 +171,6 @@ static inline bool feature_peer_smi_tx(struct ll_conn *conn)
* tone_ext * tone_ext
* per_adv_sync_tx * per_adv_sync_tx
* per_adv_sync_rx * per_adv_sync_rx
* sleep_upd
* rpk_valid * rpk_valid
* iso_central * iso_central
* iso_periph * iso_periph

View file

@ -28,6 +28,7 @@ enum llcp_proc {
PROC_CTE_REQ, PROC_CTE_REQ,
PROC_CIS_CREATE, PROC_CIS_CREATE,
PROC_CIS_TERMINATE, PROC_CIS_TERMINATE,
PROC_SCA_UPDATE,
/* A helper enum entry, to use in pause procedure context */ /* A helper enum entry, to use in pause procedure context */
PROC_NONE = 0x0, PROC_NONE = 0x0,
}; };
@ -295,6 +296,12 @@ struct proc_ctx {
uint8_t cis_id; uint8_t cis_id;
uint8_t error_code; uint8_t error_code;
} cis_term; } 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; } data;
struct { struct {
@ -670,6 +677,17 @@ void llcp_ntf_encode_length_change(struct ll_conn *conn,
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */ #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) #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
/* /*
* Constant Tone Request Procedure Helper * Constant Tone Request Procedure Helper

View file

@ -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); llcp_lp_comm_rx(conn, ctx, rx);
break; break;
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); LL_ASSERT(0);
@ -321,6 +326,11 @@ static void lr_act_run(struct ll_conn *conn)
llcp_lp_comm_run(conn, ctx, NULL); llcp_lp_comm_run(conn, ctx, NULL);
break; break;
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); LL_ASSERT(0);

View file

@ -23,6 +23,7 @@
#include "ll_settings.h" #include "ll_settings.h"
#include "lll.h" #include "lll.h"
#include "lll_clock.h"
#include "lll/lll_df_types.h" #include "lll/lll_df_types.h"
#include "lll_conn.h" #include "lll_conn.h"
#include "lll_conn_iso.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; 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) */ #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 */

View file

@ -94,6 +94,7 @@ static bool proc_with_instant(struct proc_ctx *ctx)
case PROC_CTE_REQ: case PROC_CTE_REQ:
case PROC_CIS_TERMINATE: case PROC_CIS_TERMINATE:
case PROC_CIS_CREATE: case PROC_CIS_CREATE:
case PROC_SCA_UPDATE:
return 0U; return 0U;
case PROC_PHY_UPDATE: case PROC_PHY_UPDATE:
case PROC_CONN_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); llcp_rp_comm_rx(conn, ctx, rx);
break; break;
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); 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: case PROC_CTE_REQ:
llcp_rp_comm_tx_ack(conn, ctx, tx); llcp_rp_comm_tx_ack(conn, ctx, tx);
break; 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 */ #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */
default: default:
/* Ignore tx_ack */ /* Ignore tx_ack */
@ -399,6 +410,11 @@ static void rr_act_run(struct ll_conn *conn)
llcp_rp_comm_run(conn, ctx, NULL); llcp_rp_comm_run(conn, ctx, NULL);
break; break;
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO || CONFIG_BT_CTLR_PERIPHERAL_ISO */ #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: default:
/* Unknown procedure */ /* Unknown procedure */
LL_ASSERT(0); LL_ASSERT(0);
@ -831,6 +847,9 @@ static const struct proc_role new_proc_lut[] = {
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) #if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
[PDU_DATA_LLCTRL_TYPE_CIS_REQ] = { PROC_CIS_CREATE, ACCEPT_ROLE_PERIPHERAL }, [PDU_DATA_LLCTRL_TYPE_CIS_REQ] = { PROC_CIS_CREATE, ACCEPT_ROLE_PERIPHERAL },
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ #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) void llcp_rr_new(struct ll_conn *conn, struct node_rx_pdu *rx, bool valid_pdu)

View file

@ -165,7 +165,7 @@
#define FEAT_PER_ADV_SYNC_RX 0x00 #define FEAT_PER_ADV_SYNC_RX 0x00
#endif #endif
#if defined(CONFIG_MISSING) #if defined(CONFIG_BT_CTLR_SCA_UPDATE)
#define FEAT_SLEEP_UPD 0x4000000 #define FEAT_SLEEP_UPD 0x4000000
#else #else
#define FEAT_SLEEP_UPD 0x00 #define FEAT_SLEEP_UPD 0x00

View file

@ -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_ind(struct pdu_data *pdu, void *param);
void helper_pdu_encode_cis_terminate_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_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); 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 helper_pdu_verify_cis_terminate_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param); 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 { enum helper_pdu_opcode {
LL_VERSION_IND, LL_VERSION_IND,
LL_LE_PING_REQ, LL_LE_PING_REQ,
@ -178,6 +187,8 @@ enum helper_pdu_opcode {
LL_LENGTH_RSP, LL_LENGTH_RSP,
LL_CTE_REQ, LL_CTE_REQ,
LL_CTE_RSP, LL_CTE_RSP,
LL_CLOCK_ACCURACY_REQ,
LL_CLOCK_ACCURACY_RSP,
LL_CIS_REQ, LL_CIS_REQ,
LL_CIS_RSP, LL_CIS_RSP,
LL_CIS_IND, LL_CIS_IND,
@ -192,7 +203,7 @@ enum helper_node_opcode {
NODE_CTE_RSP, NODE_CTE_RSP,
NODE_CIS_REQUEST, NODE_CIS_REQUEST,
NODE_CIS_ESTABLISHED, NODE_CIS_ESTABLISHED,
NODE_PEER_SCA_UPDATE,
}; };
typedef void(helper_pdu_encode_func_t)(struct pdu_data *data, void *param); typedef void(helper_pdu_encode_func_t)(struct pdu_data *data, void *param);

View file

@ -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; 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 helper_pdu_verify_version_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param) 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); 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 helper_pdu_verify_unknown_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param) 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, zassert_equal(pdu->llctrl.cis_terminate_ind.error_code, p->error_code,
"Error code mismatch.\nCalled at %s:%d\n", file, line); "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);
}

View file

@ -87,6 +87,8 @@ helper_pdu_encode_func_t *const helper_pdu_encode[] = {
[LL_LENGTH_RSP] = helper_pdu_encode_length_rsp, [LL_LENGTH_RSP] = helper_pdu_encode_length_rsp,
[LL_CTE_REQ] = helper_pdu_encode_cte_req, [LL_CTE_REQ] = helper_pdu_encode_cte_req,
[LL_CTE_RSP] = helper_pdu_encode_cte_rsp, [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_REQ] = helper_pdu_encode_cis_req,
[LL_CIS_RSP] = helper_pdu_encode_cis_rsp, [LL_CIS_RSP] = helper_pdu_encode_cis_rsp,
[LL_CIS_IND] = helper_pdu_encode_cis_ind, [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_LENGTH_RSP] = helper_pdu_verify_length_rsp,
[LL_CTE_REQ] = helper_pdu_verify_cte_req, [LL_CTE_REQ] = helper_pdu_verify_cte_req,
[LL_CTE_RSP] = helper_pdu_verify_cte_rsp, [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_REQ] = helper_pdu_verify_cis_req,
[LL_CIS_RSP] = helper_pdu_verify_cis_rsp, [LL_CIS_RSP] = helper_pdu_verify_cis_rsp,
[LL_CIS_IND] = helper_pdu_verify_cis_ind, [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_REQ] = NULL,
[LL_CTE_RSP] = helper_pdu_ntf_verify_cte_rsp, [LL_CTE_RSP] = helper_pdu_ntf_verify_cte_rsp,
[LL_CTE_RSP] = NULL, [LL_CTE_RSP] = NULL,
[LL_CLOCK_ACCURACY_REQ] = NULL,
[LL_CLOCK_ACCURACY_RSP] = NULL,
[LL_CIS_REQ] = NULL, [LL_CIS_REQ] = NULL,
[LL_CIS_RSP] = NULL, [LL_CIS_RSP] = NULL,
[LL_CIS_IND] = 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_CHAN_MAP_UPDATE_IND] = NULL,
[LL_CTE_REQ] = NULL, [LL_CTE_REQ] = NULL,
[LL_CTE_RSP] = helper_node_encode_cte_rsp, [LL_CTE_RSP] = helper_node_encode_cte_rsp,
[LL_CLOCK_ACCURACY_REQ] = NULL,
[LL_CLOCK_ACCURACY_RSP] = NULL,
[LL_CIS_REQ] = NULL, [LL_CIS_REQ] = NULL,
[LL_CIS_RSP] = NULL, [LL_CIS_RSP] = NULL,
[LL_CIS_IND] = 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_CTE_RSP] = helper_node_verify_cte_rsp,
[NODE_CIS_REQUEST] = helper_node_verify_cis_request, [NODE_CIS_REQUEST] = helper_node_verify_cis_request,
[NODE_CIS_ESTABLISHED] = helper_node_verify_cis_established, [NODE_CIS_ESTABLISHED] = helper_node_verify_cis_established,
[NODE_PEER_SCA_UPDATE] = helper_node_verify_peer_sca_update,
}; };
/* /*

View file

@ -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})

View file

@ -0,0 +1,568 @@
/*
* Copyright (c) 2022 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <zephyr/ztest.h>
#include "kconfig.h"
#include <zephyr/bluetooth/hci.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/slist.h>
#include <zephyr/sys/util.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_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_<INVALID>_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_<INVALID>_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);
}

View file

@ -0,0 +1,5 @@
common:
tags: test_framework bluetooth bt_sca_update bt_ull_llcp
tests:
bluetooth.controller.ctrl_sca_update.test:
type: unit

View file

@ -162,6 +162,10 @@
#define CONFIG_BT_CTLR_DF_CTE_RX y #define CONFIG_BT_CTLR_DF_CTE_RX y
#endif #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 #ifndef CONFIG_BT_CTLR_DF_MAX_ANT_SW_PATTERN_LEN
#define CONFIG_BT_CTLR_DF_MAX_ANT_SW_PATTERN_LEN 38 #define CONFIG_BT_CTLR_DF_MAX_ANT_SW_PATTERN_LEN 38
#endif #endif

View file

@ -41,6 +41,11 @@ int lll_hfclock_off(void)
return 0; return 0;
} }
uint8_t lll_clock_sca_local_get(void)
{
return 0;
}
uint32_t lll_clock_ppm_local_get(void) uint32_t lll_clock_ppm_local_get(void)
{ {
return 0; return 0;