Bluetooth: controller: Implement cross connection CPR mutual exclusion

Porting mechanism as used in legacy LLCP to refactored CPR.

Signed-off-by: Erik Brockhoff <erbr@oticon.com>
This commit is contained in:
Erik Brockhoff 2022-04-27 12:50:52 +02:00 committed by Carles Cufí
parent 4ae3dc8cb2
commit 9498310c53
4 changed files with 993 additions and 53 deletions

View file

@ -97,6 +97,7 @@ enum {
enum {
RP_CU_STATE_IDLE,
RP_CU_STATE_WAIT_RX_CONN_PARAM_REQ,
RP_CU_STATE_WAIT_CONN_PARAM_REQ_AVAILABLE,
RP_CU_STATE_WAIT_NTF_CONN_PARAM_REQ,
RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY,
RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE,
@ -202,7 +203,7 @@ static void lp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
llcp_pdu_encode_conn_param_req(ctx, pdu);
break;
case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND:
llcp_pdu_encode_reject_ext_ind(pdu, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP,
llcp_pdu_encode_reject_ext_ind(pdu, ctx->data.cu.rejected_opcode,
ctx->data.cu.error);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
@ -253,14 +254,27 @@ static void lp_cu_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
ll_rx_sched();
}
static void lp_cu_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
static void lp_cu_complete(struct ll_conn *conn, struct proc_ctx *ctx)
{
llcp_lr_complete(conn);
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
if (ctx->proc == PROC_CONN_PARAM_REQ &&
!(conn->lll.role && ull_cp_remote_cpr_pending(conn))) {
/* For a peripheral without a remote initiated CPR */
cpr_active_check_and_reset(conn);
}
#endif /* defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) */
ctx->state = LP_CU_STATE_IDLE;
}
static void lp_cu_wait_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (!llcp_ntf_alloc_is_available()) {
ctx->state = LP_CU_STATE_WAIT_NTF;
} else {
lp_cu_ntf(conn, ctx);
llcp_lr_complete(conn);
ctx->state = LP_CU_STATE_IDLE;
lp_cu_complete(conn, ctx);
}
}
@ -273,16 +287,15 @@ static void lp_cu_send_reject_ext_ind(struct ll_conn *conn, struct proc_ctx *ctx
} else {
llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION);
lp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND);
llcp_lr_complete(conn);
ctx->state = LP_CU_STATE_IDLE;
lp_cu_complete(conn, ctx);
}
}
static void lp_cu_send_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (llcp_lr_ispaused(conn) || llcp_rr_get_collision(conn) ||
!llcp_tx_alloc_peek(conn, ctx)) {
if (cpr_active_is_set(conn) || llcp_lr_ispaused(conn) ||
llcp_rr_get_collision(conn) || !llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = LP_CU_STATE_WAIT_TX_CONN_PARAM_REQ;
} else {
uint16_t event_counter = ull_conn_event_counter(conn);
@ -298,6 +311,9 @@ static void lp_cu_send_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx
ctx->data.cu.offset4 = 0xffffU;
ctx->data.cu.offset5 = 0xffffU;
/* Mark CPR as active */
cpr_active_set(conn);
lp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ);
switch (conn->lll.role) {
@ -409,6 +425,7 @@ static void lp_cu_st_wait_rx_conn_param_rsp(struct ll_conn *conn, struct proc_ct
llcp_rr_set_incompat(conn, INCOMPAT_RESERVED);
/* Perform Param check and possibly reject (LL_REJECT_EXT_IND) */
if (!cu_check_conn_parameters(conn, ctx)) {
ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP;
ctx->data.cu.error = BT_HCI_ERR_INVALID_LL_PARAM;
lp_cu_send_reject_ext_ind(conn, ctx, evt, param);
break;
@ -431,7 +448,7 @@ static void lp_cu_st_wait_rx_conn_param_rsp(struct ll_conn *conn, struct proc_ct
} else {
llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION);
ctx->data.cu.error = pdu->llctrl.reject_ext_ind.error_code;
lp_cu_complete(conn, ctx, evt, param);
lp_cu_wait_complete(conn, ctx, evt, param);
}
break;
default:
@ -468,11 +485,11 @@ static void lp_cu_st_wait_rx_conn_update_ind(struct ll_conn *conn, struct proc_c
break;
case LP_CU_EVT_UNKNOWN:
ctx->data.cu.error = BT_HCI_ERR_UNSUPP_REMOTE_FEATURE;
lp_cu_complete(conn, ctx, evt, param);
lp_cu_wait_complete(conn, ctx, evt, param);
break;
case LP_CU_EVT_REJECT:
ctx->data.cu.error = pdu->llctrl.reject_ext_ind.error_code;
lp_cu_complete(conn, ctx, evt, param);
lp_cu_wait_complete(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
@ -505,10 +522,9 @@ static void lp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint
notify = cu_should_notify_host(ctx);
if (notify) {
ctx->data.cu.error = BT_HCI_ERR_SUCCESS;
lp_cu_complete(conn, ctx, evt, param);
lp_cu_wait_complete(conn, ctx, evt, param);
} else {
llcp_lr_complete(conn);
ctx->state = LP_CU_STATE_IDLE;
lp_cu_complete(conn, ctx);
}
}
}
@ -530,7 +546,7 @@ static void lp_cu_st_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_
{
switch (evt) {
case LP_CU_EVT_RUN:
lp_cu_complete(conn, ctx, evt, param);
lp_cu_wait_complete(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
@ -605,8 +621,7 @@ void llcp_lp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pd
/* Invalid behaviour */
/* Invalid PDU received so terminate connection */
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
llcp_lr_complete(conn);
ctx->state = LP_CU_STATE_IDLE;
lp_cu_complete(conn, ctx);
break;
}
}
@ -648,7 +663,7 @@ static void rp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
break;
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND:
llcp_pdu_encode_reject_ext_ind(pdu, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ,
llcp_pdu_encode_reject_ext_ind(pdu, ctx->data.cu.rejected_opcode,
ctx->data.cu.error);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
@ -719,14 +734,25 @@ static void rp_cu_conn_param_req_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
static void rp_cu_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
static void rp_cu_complete(struct ll_conn *conn, struct proc_ctx *ctx)
{
llcp_rr_complete(conn);
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
if (ctx->proc == PROC_CONN_PARAM_REQ) {
cpr_active_check_and_reset(conn);
}
#endif /* defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) */
ctx->state = RP_CU_STATE_IDLE;
}
static void rp_cu_wait_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (!llcp_ntf_alloc_is_available()) {
ctx->state = RP_CU_STATE_WAIT_NTF;
} else {
rp_cu_ntf(conn, ctx);
llcp_rr_complete(conn);
ctx->state = RP_CU_STATE_IDLE;
rp_cu_complete(conn, ctx);
}
}
@ -754,8 +780,7 @@ static void rp_cu_send_reject_ext_ind(struct ll_conn *conn, struct proc_ctx *ctx
ctx->state = RP_CU_STATE_WAIT_TX_REJECT_EXT_IND;
} else {
rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND);
llcp_rr_complete(conn);
ctx->state = RP_CU_STATE_IDLE;
rp_cu_complete(conn, ctx);
}
}
@ -790,8 +815,7 @@ static void rp_cu_send_unknown_rsp(struct ll_conn *conn, struct proc_ctx *ctx, u
ctx->state = RP_CU_STATE_WAIT_TX_UNKNOWN_RSP;
} else {
rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP);
llcp_rr_complete(conn);
ctx->state = RP_CU_STATE_IDLE;
rp_cu_complete(conn, ctx);
}
}
@ -821,6 +845,44 @@ static void rp_cu_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t ev
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void rp_cu_st_wait_conn_param_req_available(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
/* Check if CPR is already active on other connection.
* If so check if possible to send reject right away
* otherwise stay in wait state in case CPR becomes
* available before we can send send reject
*/
switch (evt) {
case RP_CU_EVT_CONN_PARAM_REQ:
case RP_CU_EVT_RUN:
if (cpr_active_is_set(conn)) {
ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_AVAILABLE;
if (!llcp_rr_ispaused(conn) && llcp_tx_alloc_peek(conn, ctx)) {
/* We're good to reject immediately */
ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ;
ctx->data.cu.error = BT_HCI_ERR_UNSUPP_LL_PARAM_VAL;
rp_cu_send_reject_ext_ind(conn, ctx, evt, param);
}
} else {
cpr_active_set(conn);
const bool params_changed =
cu_have_params_changed(conn, ctx->data.cu.interval_max,
ctx->data.cu.latency, ctx->data.cu.timeout);
/* notify Host if conn parameters changed, else respond */
if (params_changed) {
rp_cu_send_conn_param_req_ntf(conn, ctx, evt, param);
} else {
ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY;
}
}
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_st_wait_rx_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
@ -830,21 +892,13 @@ static void rp_cu_st_wait_rx_conn_param_req(struct ll_conn *conn, struct proc_ct
/* Perform Param check and reject if invalid (LL_REJECT_EXT_IND) */
if (!cu_check_conn_parameters(conn, ctx)) {
ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ;
ctx->data.cu.error = BT_HCI_ERR_INVALID_LL_PARAM;
rp_cu_send_reject_ext_ind(conn, ctx, evt, param);
break;
}
const bool params_changed =
cu_have_params_changed(conn, ctx->data.cu.interval_max,
ctx->data.cu.latency, ctx->data.cu.timeout);
/* notify Host if conn parameters changed, else respond */
if (params_changed) {
rp_cu_send_conn_param_req_ntf(conn, ctx, evt, param);
} else {
ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY;
}
rp_cu_st_wait_conn_param_req_available(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
@ -875,6 +929,7 @@ static void rp_cu_state_wait_conn_param_req_reply(struct ll_conn *conn, struct p
break;
case RP_CU_EVT_CONN_PARAM_REQ_NEG_REPLY:
/* Send reject in next prepare run */
ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ;
ctx->state = RP_CU_STATE_WAIT_TX_REJECT_EXT_IND;
break;
default:
@ -967,10 +1022,9 @@ static void rp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint
notify = cu_should_notify_host(ctx);
if (notify) {
ctx->data.cu.error = BT_HCI_ERR_SUCCESS;
rp_cu_complete(conn, ctx, evt, param);
rp_cu_wait_complete(conn, ctx, evt, param);
} else {
llcp_rr_complete(conn);
ctx->state = RP_CU_STATE_IDLE;
rp_cu_complete(conn, ctx);
}
}
}
@ -1018,7 +1072,7 @@ static void rp_cu_st_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_
{
switch (evt) {
case RP_CU_EVT_RUN:
rp_cu_complete(conn, ctx, evt, param);
rp_cu_wait_complete(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
@ -1036,6 +1090,9 @@ static void rp_cu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_
case RP_CU_STATE_WAIT_RX_CONN_PARAM_REQ:
rp_cu_st_wait_rx_conn_param_req(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_CONN_PARAM_REQ_AVAILABLE:
rp_cu_st_wait_conn_param_req_available(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_NTF_CONN_PARAM_REQ:
rp_cu_state_wait_ntf_conn_param_req(conn, ctx, evt, param);
break;
@ -1088,8 +1145,7 @@ void llcp_rp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pd
/* Invalid behaviour */
/* Invalid PDU received so terminate connection */
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
llcp_rr_complete(conn);
ctx->state = RP_CU_STATE_IDLE;
rp_cu_complete(conn, ctx);
break;
}
}

View file

@ -196,6 +196,7 @@ struct proc_ctx {
/* Connection Update & Connection Parameter Request */
struct {
uint8_t error;
uint8_t rejected_opcode;
uint8_t params_changed;
uint16_t instant;
uint8_t win_size;

File diff suppressed because it is too large Load diff

View file

@ -31,6 +31,8 @@
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "ull_conn_internal.h"
#define EVENT_DONE_MAX 3
/* Backing storage for elements in mfifo_done */
static struct {
@ -292,6 +294,11 @@ static inline int init_reset(void)
mem_link_rx.quota_pdu = RX_CNT;
rx_alloc(UINT8_MAX);
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
/* Reset CPR mutex */
cpr_active_reset();
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
return 0;
}