Bluetooth: controller: Adding user ext. anchor point move to ctrl/CPR

Adding support for allowing user extension code to support deferring
anchor point moves. Refactored LLCP only.
Note: This is NOT supported by LEGACY LLCP impl.
KConfig'd by BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE
Modified ll_conn_update API, to accommodate passing offsets
Modified CPR/CU context data structure
Modified CPR/CU procedure to handle anchor point move wait state
Fixed up unit tests, adding anchor point move cases to CPR/CU test

Signed-off-by: Erik Brockhoff <erbr@oticon.com>
This commit is contained in:
Erik Brockhoff 2022-08-16 14:01:18 +02:00 committed by Carles Cufí
commit c180afec11
15 changed files with 842 additions and 84 deletions

View file

@ -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"

View file

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

View file

@ -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,

View file

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

View file

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

View file

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

View file

@ -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 devices 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.
*/

View file

@ -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) {
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 */

View file

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

View file

@ -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) */

View file

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

View file

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

View file

@ -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, peripherals 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, peripherals 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, peripherals 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, peripherals 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, peripherals 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 */
}

View file

@ -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"

View file

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