diff --git a/subsys/bluetooth/controller/Kconfig.ll_sw_split b/subsys/bluetooth/controller/Kconfig.ll_sw_split index 6ff0ba73e5e..73974e4f7a6 100644 --- a/subsys/bluetooth/controller/Kconfig.ll_sw_split +++ b/subsys/bluetooth/controller/Kconfig.ll_sw_split @@ -914,6 +914,14 @@ config BT_CTLR_USER_CPR_INTERVAL_MIN When enabled, controller will accept Connection Parameter Request intervals down to a proprietary minimum value. +config BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE + bool "Proprietary handling of anchor point movement" + depends on BT_CTLR_USER_EXT && BT_CTLR_CONN_PARAM_REQ && BT_PERIPHERAL + help + When enabled, defer accept/reject of Connection Parameter Request with only + anchor point movement to proprietary logic. + Only applicable for peripheral. + endmenu comment "BLE Controller debug configuration" diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c index bdf6e79f10d..11139dfa51a 100644 --- a/subsys/bluetooth/controller/hci/hci.c +++ b/subsys/bluetooth/controller/hci/hci.c @@ -2481,7 +2481,7 @@ static void le_conn_update(struct net_buf *buf, struct net_buf **evt) status = ll_conn_update(handle, 0, 0, conn_interval_min, conn_interval_max, conn_latency, - supervision_timeout); + supervision_timeout, NULL); *evt = cmd_status(status); } @@ -2505,7 +2505,7 @@ static void le_conn_param_req_reply(struct net_buf *buf, struct net_buf **evt) timeout = sys_le16_to_cpu(cmd->timeout); status = ll_conn_update(handle, 2, 0, interval_min, interval_max, - latency, timeout); + latency, timeout, NULL); rp = hci_cmd_complete(evt, sizeof(*rp)); rp->status = status; @@ -2521,7 +2521,7 @@ static void le_conn_param_req_neg_reply(struct net_buf *buf, uint8_t status; handle = sys_le16_to_cpu(cmd->handle); - status = ll_conn_update(handle, 2, cmd->reason, 0, 0, 0, 0); + status = ll_conn_update(handle, 2, cmd->reason, 0, 0, 0, 0, NULL); rp = hci_cmd_complete(evt, sizeof(*rp)); rp->status = status; @@ -8401,7 +8401,7 @@ static void le_conn_param_req(struct pdu_data *pdu_data, uint16_t handle, !(le_event_mask & BT_EVT_MASK_LE_CONN_PARAM_REQ)) { /* event masked, reject the conn param req */ ll_conn_update(handle, 2, BT_HCI_ERR_UNSUPP_REMOTE_FEATURE, 0, - 0, 0, 0); + 0, 0, 0, NULL); return; } diff --git a/subsys/bluetooth/controller/include/ll.h b/subsys/bluetooth/controller/include/ll.h index 2d848ea3211..be3df3df691 100644 --- a/subsys/bluetooth/controller/include/ll.h +++ b/subsys/bluetooth/controller/include/ll.h @@ -239,7 +239,7 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window, #endif /* !CONFIG_BT_CTLR_ADV_EXT */ uint8_t ll_connect_disable(void **rx); uint8_t ll_conn_update(uint16_t handle, uint8_t cmd, uint8_t status, uint16_t interval_min, - uint16_t interval_max, uint16_t latency, uint16_t timeout); + uint16_t interval_max, uint16_t latency, uint16_t timeout, uint16_t *offset); uint8_t ll_chm_update(uint8_t const *const chm); uint8_t ll_chm_get(uint16_t handle, uint8_t *const chm); uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand_num, uint8_t const *const ediv, diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn.c b/subsys/bluetooth/controller/ll_sw/ull_conn.c index 9d059c3c225..5a07365ecec 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn.c +++ b/subsys/bluetooth/controller/ll_sw/ull_conn.c @@ -390,7 +390,7 @@ int ll_tx_mem_enqueue(uint16_t handle, void *tx) } uint8_t ll_conn_update(uint16_t handle, uint8_t cmd, uint8_t status, uint16_t interval_min, - uint16_t interval_max, uint16_t latency, uint16_t timeout) + uint16_t interval_max, uint16_t latency, uint16_t timeout, uint16_t *offset) { struct ll_conn *conn; @@ -400,6 +400,8 @@ uint8_t ll_conn_update(uint16_t handle, uint8_t cmd, uint8_t status, uint16_t in } #if defined(CONFIG_BT_LL_SW_LLCP_LEGACY) + /* Anchor point move not supported in Legacy LLCP */ + ARG_UNUSED(offset); if (!cmd) { #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) if (!conn->llcp_conn_param.disabled && @@ -476,7 +478,8 @@ uint8_t ll_conn_update(uint16_t handle, uint8_t cmd, uint8_t status, uint16_t in if (cmd == 0U) { uint8_t err; - err = ull_cp_conn_update(conn, interval_min, interval_max, latency, timeout); + err = ull_cp_conn_update(conn, interval_min, interval_max, latency, timeout, + offset); if (err) { return err; } diff --git a/subsys/bluetooth/controller/ll_sw/ull_internal.h b/subsys/bluetooth/controller/ll_sw/ull_internal.h index 6208e85f959..fc1513a4695 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_internal.h @@ -15,6 +15,30 @@ #define CONN_INTERVAL_MIN(x) (MAX(ull_conn_interval_min_get(x), 1)) #endif /* CONFIG_BT_CTLR_USER_CPR_INTERVAL_MIN */ +/** + * User deferance of CPR Anchor Point Move + */ +#if !defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) +#define DEFER_APM_CHECK(x, y, z) (false) +#else +/* Proprietary handling of peripheral CPR Anchor Point Movement Response + * + * When returning TRUE the LLCP system changes to a + * USER_WAIT state and an EXTERNAL trigger must kick the LLCP system + * to continue to either accept (with possibly changed offsets) or reject CPR + * + * When returning FALSE the LLCP system will automatically + * continue and thus respond immediately + * + * Possibly modified LLCP internal status/error state will determine the type of 'response' + * 0U - Accept CPR (possibly with changed offsets) + * BT_HCI_ERR_UNSUPP_LL_PARAM_VAL - Reject CPR + */ +struct ll_conn; +extern bool ull_handle_cpr_anchor_point_move(struct ll_conn *conn, uint16_t *offsets, + uint8_t *status); +#define DEFER_APM_CHECK(x, y, z) (ull_handle_cpr_anchor_point_move(x, y, z)) +#endif /* CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE */ /* Macro to convert time in us to connection interval units */ #define RADIO_CONN_EVENTS(x, y) ((uint16_t)(((x) + (y) - 1) / (y))) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.c b/subsys/bluetooth/controller/ll_sw/ull_llcp.c index 4f1a1755835..dcccee29765 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.c @@ -972,7 +972,7 @@ uint8_t ull_cp_ltk_req_neq_reply(struct ll_conn *conn) #endif /* CONFIG_BT_CTLR_LE_ENC */ uint8_t ull_cp_conn_update(struct ll_conn *conn, uint16_t interval_min, uint16_t interval_max, - uint16_t latency, uint16_t timeout) + uint16_t latency, uint16_t timeout, uint16_t *offsets) { struct proc_ctx *ctx; @@ -1006,6 +1006,12 @@ uint8_t ull_cp_conn_update(struct ll_conn *conn, uint16_t interval_min, uint16_t ctx->data.cu.interval_max = interval_max; ctx->data.cu.latency = latency; ctx->data.cu.timeout = timeout; + ctx->data.cu.offsets[0] = offsets ? offsets[0] : 0x0000; + ctx->data.cu.offsets[1] = offsets ? offsets[1] : 0xffff; + ctx->data.cu.offsets[2] = offsets ? offsets[2] : 0xffff; + ctx->data.cu.offsets[3] = offsets ? offsets[3] : 0xffff; + ctx->data.cu.offsets[4] = offsets ? offsets[4] : 0xffff; + ctx->data.cu.offsets[5] = offsets ? offsets[5] : 0xffff; if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && (conn->lll.role == BT_HCI_ROLE_PERIPHERAL)) { @@ -1064,6 +1070,51 @@ uint8_t ull_cp_remote_cpr_pending(struct ll_conn *conn) return (ctx && ctx->proc == PROC_CONN_PARAM_REQ); } + +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) +bool ull_cp_remote_cpr_apm_awaiting_reply(struct ll_conn *conn) +{ + struct proc_ctx *ctx; + + ctx = llcp_rr_peek(conn); + + if (ctx && ctx->proc == PROC_CONN_PARAM_REQ) { + return llcp_rp_conn_param_req_apm_awaiting_reply(ctx); + } + + return false; +} + +void ull_cp_remote_cpr_apm_reply(struct ll_conn *conn, uint16_t *offsets) +{ + struct proc_ctx *ctx; + + ctx = llcp_rr_peek(conn); + + if (ctx && ctx->proc == PROC_CONN_PARAM_REQ) { + ctx->data.cu.offsets[0] = offsets[0]; + ctx->data.cu.offsets[1] = offsets[1]; + ctx->data.cu.offsets[2] = offsets[2]; + ctx->data.cu.offsets[3] = offsets[3]; + ctx->data.cu.offsets[4] = offsets[4]; + ctx->data.cu.offsets[5] = offsets[5]; + ctx->data.cu.error = 0U; + llcp_rp_conn_param_req_apm_reply(conn, ctx); + } +} + +void ull_cp_remote_cpr_apm_neg_reply(struct ll_conn *conn, uint8_t error_code) +{ + struct proc_ctx *ctx; + + ctx = llcp_rr_peek(conn); + + if (ctx && ctx->proc == PROC_CONN_PARAM_REQ) { + ctx->data.cu.error = error_code; + llcp_rp_conn_param_req_apm_reply(conn, ctx); + } +} +#endif /* CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE */ #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.h b/subsys/bluetooth/controller/ll_sw/ull_llcp.h index 698febec4e9..3ed1c91a002 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.h @@ -129,7 +129,7 @@ uint8_t ull_cp_phy_update(struct ll_conn *conn, uint8_t tx, uint8_t flags, uint8 * @brief Initiate a Connection Parameter Request Procedure or Connection Update Procedure */ uint8_t ull_cp_conn_update(struct ll_conn *conn, uint16_t interval_min, uint16_t interval_max, - uint16_t latency, uint16_t timeout); + uint16_t latency, uint16_t timeout, uint16_t *offsets); /** * @brief Accept the remote device’s request to change connection parameters. @@ -152,6 +152,24 @@ uint8_t ull_cp_remote_dle_pending(struct ll_conn *conn); */ uint8_t ull_cp_remote_cpr_pending(struct ll_conn *conn); +/** + * @brief Check if a remote connection param reg is expecting an + * anchor point move response. + */ +bool ull_cp_remote_cpr_apm_awaiting_reply(struct ll_conn *conn); + +/** + * @brief Repsond to anchor point move of remote connection + * param reg. + */ +void ull_cp_remote_cpr_apm_reply(struct ll_conn *conn, uint16_t *offsets); + +/** + * @brief Reject anchor point move of remote connection param + * reg. + */ +void ull_cp_remote_cpr_apm_neg_reply(struct ll_conn *conn, uint8_t error_code); + /** * @brief Initiate a Termination Procedure. */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c index 49551c2f28a..897c57c775d 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c @@ -115,6 +115,7 @@ enum { RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY, RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE, RP_CU_STATE_WAIT_TX_REJECT_EXT_IND, + RP_CU_STATE_WAIT_USER_REPLY, RP_CU_STATE_WAIT_TX_CONN_PARAM_RSP, RP_CU_STATE_WAIT_TX_CONN_UPDATE_IND, RP_CU_STATE_WAIT_RX_CONN_UPDATE_IND, @@ -139,6 +140,9 @@ enum { /* CONN_PARAM_REQ negative reply */ RP_CU_EVT_CONN_PARAM_REQ_NEG_REPLY, + + /* CONN_PARAM_REQ Ancjor Point Move reply */ + RP_CU_EVT_CONN_PARAM_REQ_USER_REPLY, }; /* @@ -197,7 +201,6 @@ static void cu_prepare_update_ind(struct ll_conn *conn, struct proc_ctx *ctx) ctx->data.cu.win_size = 1U; ctx->data.cu.win_offset_us = 0U; - ctx->data.cu.instant = ull_conn_event_counter(conn) + conn->lll.latency + CONN_UPDATE_INSTANT_DELTA; } @@ -336,12 +339,6 @@ static void lp_cu_send_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx ctx->data.cu.reference_conn_event_count = event_counter; ctx->data.cu.preferred_periodicity = 0U; - ctx->data.cu.offset0 = 0x0000U; - ctx->data.cu.offset1 = 0xffffU; - ctx->data.cu.offset2 = 0xffffU; - ctx->data.cu.offset3 = 0xffffU; - ctx->data.cu.offset4 = 0xffffU; - ctx->data.cu.offset5 = 0xffffU; /* Mark CPR as active */ cpr_active_set(conn); @@ -908,6 +905,16 @@ static void rp_cu_st_wait_conn_param_req_available(struct ll_conn *conn, struct if (params_changed) { rp_cu_send_conn_param_req_ntf(conn, ctx, evt, param); } else { +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) + /* Handle APM as a vendor specific user extension */ + if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL && + DEFER_APM_CHECK(conn, ctx->data.cu.offsets, + &ctx->data.cu.error)) { + /* Wait for user response */ + ctx->state = RP_CU_STATE_WAIT_USER_REPLY; + break; + } +#endif /* CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE */ ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE; } } @@ -917,8 +924,8 @@ static void rp_cu_st_wait_conn_param_req_available(struct ll_conn *conn, struct } } -static void rp_cu_st_wait_rx_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, - void *param) +static void rp_cu_st_wait_rx_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, + uint8_t evt, void *param) { switch (evt) { case RP_CU_EVT_CONN_PARAM_REQ: @@ -981,7 +988,13 @@ static void rp_cu_state_wait_conn_param_req_reply_continue(struct ll_conn *conn, if (conn->lll.role == BT_HCI_ROLE_CENTRAL) { rp_cu_send_conn_update_ind(conn, ctx, evt, param); } else if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL) { - rp_cu_send_conn_param_rsp(conn, ctx, evt, param); + if (!ctx->data.cu.error) { + rp_cu_send_conn_param_rsp(conn, ctx, evt, param); + } else { + ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ; + rp_cu_send_reject_ext_ind(conn, ctx, evt, param); + + } } else { /* Unknown role */ LL_ASSERT(0); @@ -1018,6 +1031,22 @@ static void rp_cu_st_wait_tx_conn_param_rsp(struct ll_conn *conn, struct proc_ct break; } } + +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) +static void rp_cu_st_wait_user_response(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case RP_CU_EVT_CONN_PARAM_REQ_USER_REPLY: + /* Continue procedure in next prepare run */ + ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE; + break; + default: + /* Ignore other evts */ + break; + } +} +#endif /* CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE */ #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ static void rp_cu_st_wait_tx_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx, @@ -1151,6 +1180,11 @@ static void rp_cu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_ case RP_CU_STATE_WAIT_TX_CONN_PARAM_RSP: rp_cu_st_wait_tx_conn_param_rsp(conn, ctx, evt, param); break; +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) + case RP_CU_STATE_WAIT_USER_REPLY: + rp_cu_st_wait_user_response(conn, ctx, evt, param); + break; +#endif /* CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE */ #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ case RP_CU_STATE_WAIT_TX_CONN_UPDATE_IND: rp_cu_st_wait_tx_conn_update_ind(conn, ctx, evt, param); @@ -1213,4 +1247,16 @@ void llcp_rp_conn_param_req_neg_reply(struct ll_conn *conn, struct proc_ctx *ctx { rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_PARAM_REQ_NEG_REPLY, NULL); } + +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) +bool llcp_rp_conn_param_req_apm_awaiting_reply(struct proc_ctx *ctx) +{ + return (ctx->state == RP_CU_STATE_WAIT_USER_REPLY); +} + +void llcp_rp_conn_param_req_apm_reply(struct ll_conn *conn, struct proc_ctx *ctx) +{ + rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_PARAM_REQ_USER_REPLY, NULL); +} +#endif /* CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE */ #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h index 3de36405ce2..538b1354218 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h @@ -219,12 +219,7 @@ struct proc_ctx { #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) uint8_t preferred_periodicity; uint16_t reference_conn_event_count; - uint16_t offset0; - uint16_t offset1; - uint16_t offset2; - uint16_t offset3; - uint16_t offset4; - uint16_t offset5; + uint16_t offsets[6]; #endif /* defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) */ } cu; @@ -498,6 +493,8 @@ void llcp_rp_cu_init_proc(struct proc_ctx *ctx); void llcp_rp_cu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param); void llcp_rp_conn_param_req_reply(struct ll_conn *conn, struct proc_ctx *ctx); void llcp_rp_conn_param_req_neg_reply(struct ll_conn *conn, struct proc_ctx *ctx); +bool llcp_rp_conn_param_req_apm_awaiting_reply(struct proc_ctx *ctx); +void llcp_rp_conn_param_req_apm_reply(struct ll_conn *conn, struct proc_ctx *ctx); /* * Terminate Helper diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c index db7d07cf090..947f037d107 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c @@ -505,12 +505,12 @@ void llcp_pdu_encode_conn_param_req(struct proc_ctx *ctx, struct pdu_data *pdu) p->timeout = sys_cpu_to_le16(ctx->data.cu.timeout); p->preferred_periodicity = ctx->data.cu.preferred_periodicity; p->reference_conn_event_count = sys_cpu_to_le16(ctx->data.cu.reference_conn_event_count); - p->offset0 = sys_cpu_to_le16(ctx->data.cu.offset0); - p->offset1 = sys_cpu_to_le16(ctx->data.cu.offset1); - p->offset2 = sys_cpu_to_le16(ctx->data.cu.offset2); - p->offset3 = sys_cpu_to_le16(ctx->data.cu.offset3); - p->offset4 = sys_cpu_to_le16(ctx->data.cu.offset4); - p->offset5 = sys_cpu_to_le16(ctx->data.cu.offset5); + p->offset0 = sys_cpu_to_le16(ctx->data.cu.offsets[0]); + p->offset1 = sys_cpu_to_le16(ctx->data.cu.offsets[1]); + p->offset2 = sys_cpu_to_le16(ctx->data.cu.offsets[2]); + p->offset3 = sys_cpu_to_le16(ctx->data.cu.offsets[3]); + p->offset4 = sys_cpu_to_le16(ctx->data.cu.offsets[4]); + p->offset5 = sys_cpu_to_le16(ctx->data.cu.offsets[5]); } void llcp_pdu_encode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) @@ -528,12 +528,12 @@ void llcp_pdu_encode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) p->timeout = sys_cpu_to_le16(ctx->data.cu.timeout); p->preferred_periodicity = ctx->data.cu.preferred_periodicity; p->reference_conn_event_count = sys_cpu_to_le16(ctx->data.cu.reference_conn_event_count); - p->offset0 = sys_cpu_to_le16(ctx->data.cu.offset0); - p->offset1 = sys_cpu_to_le16(ctx->data.cu.offset1); - p->offset2 = sys_cpu_to_le16(ctx->data.cu.offset2); - p->offset3 = sys_cpu_to_le16(ctx->data.cu.offset3); - p->offset4 = sys_cpu_to_le16(ctx->data.cu.offset4); - p->offset5 = sys_cpu_to_le16(ctx->data.cu.offset5); + p->offset0 = sys_cpu_to_le16(ctx->data.cu.offsets[0]); + p->offset1 = sys_cpu_to_le16(ctx->data.cu.offsets[1]); + p->offset2 = sys_cpu_to_le16(ctx->data.cu.offsets[2]); + p->offset3 = sys_cpu_to_le16(ctx->data.cu.offsets[3]); + p->offset4 = sys_cpu_to_le16(ctx->data.cu.offsets[4]); + p->offset5 = sys_cpu_to_le16(ctx->data.cu.offsets[5]); } void llcp_pdu_decode_conn_param_req(struct proc_ctx *ctx, struct pdu_data *pdu) @@ -547,12 +547,12 @@ void llcp_pdu_decode_conn_param_req(struct proc_ctx *ctx, struct pdu_data *pdu) ctx->data.cu.timeout = sys_le16_to_cpu(p->timeout); ctx->data.cu.preferred_periodicity = p->preferred_periodicity; ctx->data.cu.reference_conn_event_count = sys_le16_to_cpu(p->reference_conn_event_count); - ctx->data.cu.offset0 = sys_le16_to_cpu(p->offset0); - ctx->data.cu.offset1 = sys_le16_to_cpu(p->offset1); - ctx->data.cu.offset2 = sys_le16_to_cpu(p->offset2); - ctx->data.cu.offset3 = sys_le16_to_cpu(p->offset3); - ctx->data.cu.offset4 = sys_le16_to_cpu(p->offset4); - ctx->data.cu.offset5 = sys_le16_to_cpu(p->offset5); + ctx->data.cu.offsets[0] = sys_le16_to_cpu(p->offset0); + ctx->data.cu.offsets[1] = sys_le16_to_cpu(p->offset1); + ctx->data.cu.offsets[2] = sys_le16_to_cpu(p->offset2); + ctx->data.cu.offsets[3] = sys_le16_to_cpu(p->offset3); + ctx->data.cu.offsets[4] = sys_le16_to_cpu(p->offset4); + ctx->data.cu.offsets[5] = sys_le16_to_cpu(p->offset5); } void llcp_pdu_decode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) @@ -566,12 +566,12 @@ void llcp_pdu_decode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) ctx->data.cu.timeout = sys_le16_to_cpu(p->timeout); ctx->data.cu.preferred_periodicity = p->preferred_periodicity; ctx->data.cu.reference_conn_event_count = sys_le16_to_cpu(p->reference_conn_event_count); - ctx->data.cu.offset0 = sys_le16_to_cpu(p->offset0); - ctx->data.cu.offset1 = sys_le16_to_cpu(p->offset1); - ctx->data.cu.offset2 = sys_le16_to_cpu(p->offset2); - ctx->data.cu.offset3 = sys_le16_to_cpu(p->offset3); - ctx->data.cu.offset4 = sys_le16_to_cpu(p->offset4); - ctx->data.cu.offset5 = sys_le16_to_cpu(p->offset5); + ctx->data.cu.offsets[0] = sys_le16_to_cpu(p->offset0); + ctx->data.cu.offsets[1] = sys_le16_to_cpu(p->offset1); + ctx->data.cu.offsets[2] = sys_le16_to_cpu(p->offset2); + ctx->data.cu.offsets[3] = sys_le16_to_cpu(p->offset3); + ctx->data.cu.offsets[4] = sys_le16_to_cpu(p->offset4); + ctx->data.cu.offsets[5] = sys_le16_to_cpu(p->offset5); } #endif /* defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) */ diff --git a/tests/bluetooth/controller/ctrl_cis_create/src/main.c b/tests/bluetooth/controller/ctrl_cis_create/src/main.c index a14014dab8a..de0646afd26 100644 --- a/tests/bluetooth/controller/ctrl_cis_create/src/main.c +++ b/tests/bluetooth/controller/ctrl_cis_create/src/main.c @@ -204,9 +204,6 @@ static void test_cc_create_periph_rem_host_accept(void) /* Done */ event_done(&conn); - /* There should NOT be a host notification */ - ut_rx_q_is_empty(); - /* Prepare */ event_prepare(&conn); diff --git a/tests/bluetooth/controller/ctrl_conn_update/src/kconfig_override_apm.h b/tests/bluetooth/controller/ctrl_conn_update/src/kconfig_override_apm.h new file mode 100644 index 00000000000..7ea344a56ae --- /dev/null +++ b/tests/bluetooth/controller/ctrl_conn_update/src/kconfig_override_apm.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2021 Demant + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Override common Kconfig settings + */ + +#ifndef CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE +#define CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE +#endif diff --git a/tests/bluetooth/controller/ctrl_conn_update/src/main.c b/tests/bluetooth/controller/ctrl_conn_update/src/main.c index 84b5240e7df..78ff956e0b7 100644 --- a/tests/bluetooth/controller/ctrl_conn_update/src/main.c +++ b/tests/bluetooth/controller/ctrl_conn_update/src/main.c @@ -165,7 +165,16 @@ struct pdu_data_llctrl_conn_update_ind *cu_ind_B = &conn_update_ind_B; static struct ll_conn conn; + #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) +bool ull_handle_cpr_anchor_point_move(struct ll_conn *conn, uint16_t *offsets, uint8_t *status) +{ + ztest_copy_return_data(status, 1); + return ztest_get_return_value(); +} +#endif /* CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE */ + static void test_unmask_feature_conn_param_req(struct ll_conn *conn) { conn->llcp.fex.features_used &= ~BIT64(BT_LE_FEAT_BIT_CONN_PARAM_REQ); @@ -187,6 +196,7 @@ static void setup(void) lll->interval = 0; lll->latency = 0; conn.supervision_reload = 1U; + lll->event_counter = 0; } static bool is_instant_reached(struct ll_conn *conn, uint16_t instant) @@ -238,7 +248,7 @@ void test_conn_update_central_loc_accept(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -311,6 +321,7 @@ void test_conn_update_central_loc_accept(void) "Free CTX buffers %d", ctx_buffers_free()); } + /* * Central-initiated Connection Parameters Request procedure. * Central requests change in LE connection parameters, peripheral’s Host accepts. @@ -390,7 +401,7 @@ void test_conn_update_central_loc_accept_reject_2nd_cpr(void) ull_cp_state_set(&conn_3rd, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -473,7 +484,7 @@ void test_conn_update_central_loc_accept_reject_2nd_cpr(void) ull_cp_release_tx(&conn_3rd, tx); /* Initiate a parallel Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn_3rd, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn_3rd, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -611,7 +622,7 @@ void test_conn_update_central_loc_invalid_param_rsp(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -680,7 +691,7 @@ void test_conn_update_central_loc_invalid_rsp(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -753,7 +764,7 @@ void test_conn_update_central_loc_reject(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -832,7 +843,7 @@ void test_conn_update_central_loc_remote_legacy(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -957,7 +968,7 @@ void test_conn_update_central_loc_unsupp_wo_feat_exch(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -1074,7 +1085,7 @@ void test_conn_update_central_loc_unsupp_w_feat_exch(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -1197,7 +1208,7 @@ void test_conn_update_central_loc_collision(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* (A) Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -1648,7 +1659,7 @@ void test_conn_update_central_rem_collision(void) /* (B) Initiate a Connection Parameter Request Procedure */ err = ull_cp_conn_update(&conn, req_B->interval_min, req_B->interval_max, req_B->latency, - req_B->timeout); + req_B->timeout, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -1830,7 +1841,7 @@ void test_conn_update_periph_loc_accept(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -1935,7 +1946,7 @@ void test_conn_update_periph_loc_reject(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -2014,7 +2025,7 @@ void test_conn_update_periph_loc_unsupp_feat_wo_feat_exch(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -2081,7 +2092,7 @@ void test_conn_update_periph_loc_unsupp_feat_w_feat_exch(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_UNSUPP_REMOTE_FEATURE); /* Prepare */ @@ -2175,7 +2186,7 @@ void test_conn_update_periph_loc_collision(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* (A) Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -2422,6 +2433,584 @@ void test_conn_update_periph_rem_accept(void) zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), "Free CTX buffers %d", ctx_buffers_free()); } +#define RADIO_CONN_EVENTS(x, y) ((uint16_t)(((x) + (y) - 1) / (y))) + +/* + * Central-initiated Connection Parameters Request procedure - only anchor point move. + * Central requests change in anchor point only on LE connection, peripheral’s Host accepts. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_P | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CONNECTION_PARAM_REQ | + * | | (only apm) | + * | |<--------------------------| + * | | | + * | Defered APM disabled | | + * | '<---------' | | + * | So accepted right away | | + * | '--------->' | | + * | | | + * | | LL_CONNECTION_PARAM_RSP | + * | |-------------------------->| + * | | | + * | | LL_CONNECTION_UPDATE_IND | + * | |<--------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * | | | + * | | | + */ +void test_conn_update_periph_rem_apm_accept_right_away(void) +{ +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) + struct node_tx *tx; + uint16_t instant; + /* Default conn_param_req PDU */ + struct pdu_data_llctrl_conn_param_req conn_param_req_apm = { .interval_min = INTVL_MIN, + .interval_max = INTVL_MAX, + .latency = LATENCY, + .timeout = TIMEOUT, + .preferred_periodicity = 0U, + .reference_conn_event_count = 0u, + .offset0 = 0x0008U, + .offset1 = 0xffffU, + .offset2 = 0xffffU, + .offset3 = 0xffffU, + .offset4 = 0xffffU, + .offset5 = 0xffffU }; + + /* Default conn_param_rsp PDU */ + struct pdu_data_llctrl_conn_param_rsp conn_param_rsp_apm = { .interval_min = INTVL_MIN, + .interval_max = INTVL_MAX, + .latency = LATENCY, + .timeout = TIMEOUT, + .preferred_periodicity = 0U, + .reference_conn_event_count = 0u, + .offset0 = 0x008U, + .offset1 = 0xffffU, + .offset2 = 0xffffU, + .offset3 = 0xffffU, + .offset4 = 0xffffU, + .offset5 = 0xffffU }; + uint8_t error = 0; + + /* Prepare mocked call to ull_handle_cpr_anchor_point_move */ + /* No APM deferance, accept with error == 0 */ + ztest_returns_value(ull_handle_cpr_anchor_point_move, false); + ztest_return_data(ull_handle_cpr_anchor_point_move, status, &error); + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + conn.lll.interval = conn_param_req_apm.interval_max; + conn.lll.latency = conn_param_req_apm.latency; + conn.supervision_reload = RADIO_CONN_EVENTS(TIMEOUT * 10000U, conn.lll.interval * + CONN_INT_UNIT_US); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_CONNECTION_PARAM_REQ, &conn, &conn_param_req_apm); + + /* Done */ + event_done(&conn); + + /*******************/ + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CONNECTION_PARAM_RSP, &conn, &tx, &conn_param_rsp_apm); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + conn_update_ind.instant = event_counter(&conn) + 6U; + instant = conn_update_ind.instant; + lt_tx(LL_CONNECTION_UPDATE_IND, &conn, &conn_update_ind); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* */ + while (!is_instant_reached(&conn, instant)) { + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should NOT be a host notification */ + ut_rx_q_is_empty(); + } + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +#endif +} + +/* + * Central-initiated Connection Parameters Request procedure - only anchor point move. + * Central requests change in anchor point only on LE connection, peripheral’s Host accepts. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_P | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CONNECTION_PARAM_REQ | + * | | (only apm) | + * | |<--------------------------| + * | | | + * | Defered APM disabled | | + * | '<---------' | | + * | So accepted right away | | + * | but with error | | + * | '--------->' | | + * | | | + * | | LL_REJECT_EXT_IND | + * | |-------------------------->| + * | | | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * | | | + * | | | + */ +void test_conn_update_periph_rem_apm_reject_right_away(void) +{ +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) + struct node_tx *tx; + /* Default conn_param_req PDU */ + struct pdu_data_llctrl_conn_param_req conn_param_req_apm = { .interval_min = INTVL_MIN, + .interval_max = INTVL_MAX, + .latency = LATENCY, + .timeout = TIMEOUT, + .preferred_periodicity = 0U, + .reference_conn_event_count = 0u, + .offset0 = 0x0008U, + .offset1 = 0xffffU, + .offset2 = 0xffffU, + .offset3 = 0xffffU, + .offset4 = 0xffffU, + .offset5 = 0xffffU }; + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ, + .error_code = BT_HCI_ERR_UNSUPP_LL_PARAM_VAL + 1 + }; + uint8_t error = reject_ext_ind.error_code; + + /* Prepare mocked call to ull_handle_cpr_anchor_point_move */ + /* No APM deferance, reject with some error code */ + ztest_returns_value(ull_handle_cpr_anchor_point_move, false); + ztest_return_data(ull_handle_cpr_anchor_point_move, status, &error); + + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + conn.lll.interval = conn_param_req_apm.interval_max; + conn.lll.latency = conn_param_req_apm.latency; + conn.supervision_reload = RADIO_CONN_EVENTS(TIMEOUT * 10000U, conn.lll.interval * + CONN_INT_UNIT_US); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_CONNECTION_PARAM_REQ, &conn, &conn_param_req_apm); + + /* Done */ + event_done(&conn); + + /*******************/ + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_REJECT_EXT_IND, &conn, &tx, &reject_ext_ind); + lt_rx_q_is_empty(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +#endif +} + +/* + * Central-initiated Connection Parameters Request procedure - only anchor point move. + * Central requests change in anchor point only on LE connection, peripheral’s Host accepts. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_P | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CONNECTION_PARAM_REQ | + * | | (only apm) | + * | |<--------------------------| + * | | | + * | Defered APM | | + * | '<---------' | | + * | | | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * | | | + * | Defered accept | | + * | '--------->' | | + * | | | + * | | LL_CONNECTION_PARAM_RSP | + * | |-------------------------->| + * | | | + * | | LL_CONNECTION_UPDATE_IND | + * | |<--------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * | | | + * | | | + */ +void test_conn_update_periph_rem_apm_accept_defered(void) +{ +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) + uint16_t offsets[6] = { + 0x0008U, + 0xffffU, + 0xffffU, + 0xffffU, + 0xffffU, + 0xffffU + }; + struct node_tx *tx; + uint16_t instant; + /* Default conn_param_req PDU */ + struct pdu_data_llctrl_conn_param_req conn_param_req_apm = { .interval_min = INTVL_MIN, + .interval_max = INTVL_MAX, + .latency = LATENCY, + .timeout = TIMEOUT, + .preferred_periodicity = 0U, + .reference_conn_event_count = 0u, + .offset0 = 0x0004U, + .offset1 = 0xffffU, + .offset2 = 0xffffU, + .offset3 = 0xffffU, + .offset4 = 0xffffU, + .offset5 = 0xffffU }; + + /* Default conn_param_rsp PDU */ + struct pdu_data_llctrl_conn_param_rsp conn_param_rsp_apm = { .interval_min = INTVL_MIN, + .interval_max = INTVL_MAX, + .latency = LATENCY, + .timeout = TIMEOUT, + .preferred_periodicity = 0U, + .reference_conn_event_count = 0u, + .offset0 = 0x008U, + .offset1 = 0xffffU, + .offset2 = 0xffffU, + .offset3 = 0xffffU, + .offset4 = 0xffffU, + .offset5 = 0xffffU }; + uint8_t error = 0; + + /* Prepare mocked call to ull_handle_cpr_anchor_point_move */ + /* Defer APM */ + ztest_returns_value(ull_handle_cpr_anchor_point_move, true); + ztest_return_data(ull_handle_cpr_anchor_point_move, status, &error); + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + conn.lll.interval = conn_param_req_apm.interval_max; + conn.lll.latency = conn_param_req_apm.latency; + conn.supervision_reload = RADIO_CONN_EVENTS(TIMEOUT * 10000U, conn.lll.interval * + CONN_INT_UNIT_US); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_CONNECTION_PARAM_REQ, &conn, &conn_param_req_apm); + + /* Done */ + event_done(&conn); + + + /* Run a few events */ + for (int i = 0; i < 10; i++) { + + /* Prepare */ + event_prepare(&conn); + + zassert_equal(true, ull_cp_remote_cpr_apm_awaiting_reply(&conn), NULL); + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + /* Done */ + event_done(&conn); + } + + ull_cp_remote_cpr_apm_reply(&conn, offsets); + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CONNECTION_PARAM_RSP, &conn, &tx, &conn_param_rsp_apm); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + conn_update_ind.instant = event_counter(&conn) + 6U; + instant = conn_update_ind.instant; + lt_tx(LL_CONNECTION_UPDATE_IND, &conn, &conn_update_ind); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* */ + while (!is_instant_reached(&conn, instant)) { + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should NOT be a host notification */ + ut_rx_q_is_empty(); + } + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +#endif +} + +/* + * Central-initiated Connection Parameters Request procedure - only anchor point move. + * Central requests change in anchor point only on LE connection, peripheral’s Host accepts. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_P | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CONNECTION_PARAM_REQ | + * | | (only apm) | + * | |<--------------------------| + * | | | + * | Defered APM | | + * | '<---------' | | + * | | | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * | | | + * | Defered accept | | + * | but with error | | + * | '--------->' | | + * | | | + * | | LL_REJECT_EXT_IND | + * | |-------------------------->| + * | | | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * | | | + * | | | + */ +void test_conn_update_periph_rem_apm_reject_defered(void) +{ +#if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) + struct node_tx *tx; + /* Default conn_param_req PDU */ + struct pdu_data_llctrl_conn_param_req conn_param_req_apm = { .interval_min = INTVL_MIN, + .interval_max = INTVL_MAX, + .latency = LATENCY, + .timeout = TIMEOUT, + .preferred_periodicity = 0U, + .reference_conn_event_count = 0u, + .offset0 = 0x0008U, + .offset1 = 0xffffU, + .offset2 = 0xffffU, + .offset3 = 0xffffU, + .offset4 = 0xffffU, + .offset5 = 0xffffU }; + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ, + .error_code = BT_HCI_ERR_UNSUPP_LL_PARAM_VAL + }; + uint8_t error = 0U; + + /* Prepare mocked call to ull_handle_cpr_anchor_point_move */ + /* Defer APM */ + ztest_returns_value(ull_handle_cpr_anchor_point_move, true); + ztest_return_data(ull_handle_cpr_anchor_point_move, status, &error); + + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + conn.lll.interval = conn_param_req_apm.interval_max; + conn.lll.latency = conn_param_req_apm.latency; + conn.supervision_reload = RADIO_CONN_EVENTS(TIMEOUT * 10000U, conn.lll.interval * + CONN_INT_UNIT_US); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_CONNECTION_PARAM_REQ, &conn, &conn_param_req_apm); + + /* Done */ + event_done(&conn); + + /* Run a few events */ + for (int i = 0; i < 10; i++) { + + /* Prepare */ + event_prepare(&conn); + + zassert_equal(true, ull_cp_remote_cpr_apm_awaiting_reply(&conn), NULL); + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + /* Done */ + event_done(&conn); + } + + ull_cp_remote_cpr_apm_neg_reply(&conn, BT_HCI_ERR_UNSUPP_LL_PARAM_VAL); + + /* Prepare */ + event_prepare(&conn); + + /* Done */ + event_done(&conn); + + /*******************/ + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_REJECT_EXT_IND, &conn, &tx, &reject_ext_ind); + lt_rx_q_is_empty(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should be no host notification */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +#endif /* CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE */ +} /* * (A) @@ -2527,7 +3116,7 @@ void test_conn_update_periph_loc_collision_reject_2nd_cpr(void) ull_cp_state_set(&conn_3rd, ULL_CP_CONNECTED); /* (A) Initiate a Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -2654,7 +3243,7 @@ void test_conn_update_periph_loc_collision_reject_2nd_cpr(void) { /* Initiate a parallel local Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn_2nd, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn_2nd, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -2904,7 +3493,7 @@ void test_conn_update_periph_rem_accept_reject_2nd_cpr(void) { /* Initiate a parallel local Connection Parameter Request Procedure */ - err = ull_cp_conn_update(&conn_2nd, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn_2nd, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -3470,7 +4059,7 @@ void test_conn_update_periph_rem_collision(void) /* (B) Initiate a Connection Parameter Request Procedure */ err = ull_cp_conn_update(&conn, req_B->interval_min, req_B->interval_max, req_B->latency, - req_B->timeout); + req_B->timeout, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /*******************/ @@ -3514,6 +4103,7 @@ void test_conn_update_periph_rem_collision(void) event_prepare(&conn); /* (A) Rx */ + conn_update_ind.instant = event_counter(&conn) + 6U; instant = conn_update_ind.instant; lt_tx(LL_CONNECTION_UPDATE_IND, &conn, &conn_update_ind); @@ -3542,6 +4132,7 @@ void test_conn_update_periph_rem_collision(void) event_prepare(&conn); /* (B) Tx Queue should have one LL Control PDU */ + req_B->reference_conn_event_count = event_counter(&conn) - 1; lt_rx(LL_CONNECTION_PARAM_REQ, &conn, &tx, req_B); lt_rx_q_is_empty(&conn); @@ -3649,7 +4240,7 @@ void test_conn_update_central_loc_accept_no_param_req(void) do { /* Initiate a Connection Update Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS); /* Prepare */ @@ -3980,7 +4571,7 @@ void test_conn_update_periph_loc_disallowed_no_param_req(void) ull_cp_state_set(&conn, ULL_CP_CONNECTED); /* Initiate a Connection Update Procedure */ - err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); zassert_equal(err, BT_HCI_ERR_CMD_DISALLOWED); /* Prepare */ @@ -4052,6 +4643,14 @@ void test_main(void) periph_rem, ztest_unit_test_setup_teardown(test_conn_update_periph_rem_accept, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_conn_update_periph_rem_apm_accept_right_away, + setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_conn_update_periph_rem_apm_reject_right_away, + setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_conn_update_periph_rem_apm_accept_defered, + setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_conn_update_periph_rem_apm_reject_defered, + setup, unit_test_noop), ztest_unit_test_setup_teardown(test_conn_update_periph_rem_invalid_req, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_conn_update_periph_rem_invalid_ind, @@ -4067,9 +4666,7 @@ void test_main(void) ztest_run_test_suite(central_rem); ztest_run_test_suite(periph_loc); ztest_run_test_suite(periph_rem); - #else /* !CONFIG_BT_CTLR_CONN_PARAM_REQ */ - ztest_test_suite(central_loc_no_param_req, ztest_unit_test_setup_teardown( test_conn_update_central_loc_accept_no_param_req, setup, unit_test_noop)); @@ -4095,6 +4692,5 @@ void test_main(void) ztest_run_test_suite(central_rem_no_param_req); ztest_run_test_suite(periph_loc_no_param_req); ztest_run_test_suite(periph_rem_no_param_req); - #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ } diff --git a/tests/bluetooth/controller/ctrl_conn_update/testcase.yaml b/tests/bluetooth/controller/ctrl_conn_update/testcase.yaml index d2d838d2fc7..35c7bf19134 100644 --- a/tests/bluetooth/controller/ctrl_conn_update/testcase.yaml +++ b/tests/bluetooth/controller/ctrl_conn_update/testcase.yaml @@ -4,6 +4,10 @@ tests: bluetooth.controller.ctrl_conn_update.test: type: unit + bluetooth.controller.ctrl_conn_update.apm_test: + type: unit + extra_args: KCONFIG_OVERRIDE_FILE="kconfig_override_apm.h" + bluetooth.controller.ctrl_conn_update.no_param_req_test: type: unit extra_args: KCONFIG_OVERRIDE_FILE="kconfig_override.h" diff --git a/tests/bluetooth/controller/ctrl_hci/src/main.c b/tests/bluetooth/controller/ctrl_hci/src/main.c index d7c2961e9cd..d5b788c7783 100644 --- a/tests/bluetooth/controller/ctrl_hci/src/main.c +++ b/tests/bluetooth/controller/ctrl_hci/src/main.c @@ -368,7 +368,7 @@ void test_hci_conn_update(void) uint8_t err; uint8_t cmd, status; - uint16_t interval_min, interval_max, latency, timeout; + uint16_t interval_min, interval_max, latency, timeout, *offsets; uint8_t unknown_cmds[3U] = { 1U, 3U, 255U }; @@ -378,6 +378,7 @@ void test_hci_conn_update(void) interval_max = 100U; latency = 5U; timeout = 1000U; + offsets = NULL; conn_handle = ll_conn_handle_get(conn_from_pool); @@ -387,38 +388,38 @@ void test_hci_conn_update(void) /* Unknown Connection ID */ err = ll_conn_update(conn_handle + 1, cmd, status, interval_min, interval_max, latency, - timeout); + timeout, offsets); zassert_equal(err, BT_HCI_ERR_UNKNOWN_CONN_ID, "Errorcode %d", err); /* Unknown commands */ for (uint8_t i = 0U; i < sizeof(unknown_cmds); i++) { err = ll_conn_update(conn_handle, unknown_cmds[i], status, interval_min, - interval_max, latency, timeout); + interval_max, latency, timeout, offsets); zassert_equal(err, BT_HCI_ERR_UNKNOWN_CMD, "Errorcode %d", err); } /* Connection Update or Connection Parameter Req. */ conn_from_pool->llcp.fex.features_used |= BIT64(BT_LE_FEAT_BIT_CONN_PARAM_REQ); err = ll_conn_update(conn_handle, cmd, status, interval_min, interval_max, latency, - timeout); + timeout, offsets); zassert_equal(err, BT_HCI_ERR_SUCCESS, "Errorcode %d", err); conn_from_pool->llcp.fex.features_used &= ~BIT64(BT_LE_FEAT_BIT_CONN_PARAM_REQ); err = ll_conn_update(conn_handle, cmd, status, interval_min, interval_max, latency, - timeout); + timeout, offsets); zassert_equal(err, BT_HCI_ERR_SUCCESS, "Errorcode %d", err); /* Connection Parameter Req. Reply */ cmd = 2U; conn_from_pool->llcp.fex.features_used |= BIT64(BT_LE_FEAT_BIT_CONN_PARAM_REQ); err = ll_conn_update(conn_handle, cmd, status, interval_min, interval_max, latency, - timeout); + timeout, offsets); zassert_equal(err, BT_HCI_ERR_SUCCESS, "Errorcode %d", err); /* Connection Parameter Req. Neg. Reply */ status = 0x01; conn_from_pool->llcp.fex.features_used |= BIT64(BT_LE_FEAT_BIT_CONN_PARAM_REQ); - err = ll_conn_update(conn_handle, cmd, status, 0U, 0U, 0U, 0U); + err = ll_conn_update(conn_handle, cmd, status, 0U, 0U, 0U, 0U, NULL); zassert_equal(err, BT_HCI_ERR_SUCCESS, "Errorcode %d", err); }