Skip to content

Commit 8b1d50b

Browse files
erbr-otcarlescufi
authored andcommitted
Bluetooth: controller: llcp: fix issue re. missing ack of terminate ind
On remote terminate on central the conn clean-up would happen before ack of terminate ind was sent to peer. Now clean-up is 'postponed' until subsequent event. Also now data tx is paused on rx of terminate ind to ensure no data is tx'ed after rx of terminate ind Signed-off-by: Erik Brockhoff <[email protected]>
1 parent 8b912f1 commit 8b1d50b

File tree

4 files changed

+75
-23
lines changed

4 files changed

+75
-23
lines changed

subsys/bluetooth/controller/ll_sw/ull_conn.c

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,32 +1479,33 @@ void ull_conn_done(struct node_rx_event_done *done)
14791479
}
14801480
#endif /* CONFIG_BT_CTLR_LE_ENC */
14811481

1482-
/* Peripheral received terminate ind or
1482+
/* Legacy LLCP:
1483+
* Peripheral received terminate ind or
14831484
* Central received ack for the transmitted terminate ind or
14841485
* Central transmitted ack for the received terminate ind or
14851486
* there has been MIC failure
1487+
* Refactored LLCP:
1488+
* reason_final is set exactly under the above conditions
14861489
*/
14871490
reason_final = conn->llcp_terminate.reason_final;
14881491
if (reason_final && (
1492+
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
1493+
#if defined(CONFIG_BT_CENTRAL)
1494+
(((conn->llcp_terminate.req -
1495+
conn->llcp_terminate.ack) & 0xFF) ==
1496+
TERM_ACKED) ||
1497+
conn->central.terminate_ack ||
1498+
(reason_final == BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL) ||
1499+
#endif /* CONFIG_BT_CENTRAL */
14891500
#if defined(CONFIG_BT_PERIPHERAL)
1490-
lll->role ||
1501+
lll->role
14911502
#else /* CONFIG_BT_PERIPHERAL */
1492-
0 ||
1503+
false
14931504
#endif /* CONFIG_BT_PERIPHERAL */
1494-
#if defined(CONFIG_BT_CENTRAL)
1495-
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
1496-
(((conn->llcp_terminate.req -
1497-
conn->llcp_terminate.ack) & 0xFF) ==
1498-
TERM_ACKED) ||
1499-
conn->central.terminate_ack ||
1500-
(reason_final == BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL)
1501-
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
1502-
1
1503-
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
1504-
#else /* CONFIG_BT_CENTRAL */
1505-
1
1506-
#endif /* CONFIG_BT_CENTRAL */
1507-
)) {
1505+
#else /* defined(CONFIG_BT_LL_SW_LLCP_LEGACY) */
1506+
true
1507+
#endif /* !defined(CONFIG_BT_LL_SW_LLCP_LEGACY) */
1508+
)) {
15081509
conn_cleanup(conn, reason_final);
15091510

15101511
return;

subsys/bluetooth/controller/ll_sw/ull_llcp_common.c

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ enum {
7777
enum {
7878
RP_COMMON_STATE_IDLE,
7979
RP_COMMON_STATE_WAIT_RX,
80+
RP_COMMON_STATE_POSTPONE_TERMINATE,
8081
RP_COMMON_STATE_WAIT_TX,
8182
RP_COMMON_STATE_WAIT_TX_ACK,
8283
RP_COMMON_STATE_WAIT_NTF,
@@ -93,6 +94,7 @@ enum {
9394
RP_COMMON_EVT_REQUEST,
9495
};
9596

97+
9698
static void lp_comm_ntf(struct ll_conn *conn, struct proc_ctx *ctx);
9799
static void lp_comm_terminate_invalid_pdu(struct ll_conn *conn, struct proc_ctx *ctx);
98100

@@ -781,8 +783,17 @@ void llcp_lp_comm_init_proc(struct proc_ctx *ctx)
781783
void llcp_lp_comm_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
782784
{
783785
lp_comm_execute_fsm(conn, ctx, LP_COMMON_EVT_RUN, param);
786+
784787
}
785788

789+
static void rp_comm_terminate(struct ll_conn *conn, struct proc_ctx *ctx)
790+
{
791+
llcp_rr_complete(conn);
792+
ctx->state = RP_COMMON_STATE_IDLE;
793+
794+
/* Mark the connection for termination */
795+
conn->llcp_terminate.reason_final = ctx->data.term.error_code;
796+
}
786797
/*
787798
* LLCP Remote Procedure Common FSM
788799
*/
@@ -821,6 +832,8 @@ static void rp_comm_rx_decode(struct ll_conn *conn, struct proc_ctx *ctx, struct
821832
break;
822833
case PDU_DATA_LLCTRL_TYPE_TERMINATE_IND:
823834
llcp_pdu_decode_terminate_ind(ctx, pdu);
835+
/* Make sure no data is tx'ed after RX of terminate ind */
836+
llcp_tx_pause_data(conn, LLCP_TX_QUEUE_PAUSE_DATA_TERMINATE);
824837
break;
825838
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
826839
case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ:
@@ -1051,12 +1064,19 @@ static void rp_comm_send_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
10511064
break;
10521065
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN && CONFIG_BT_CENTRAL */
10531066
case PROC_TERMINATE:
1054-
/* No response */
1055-
llcp_rr_complete(conn);
1056-
ctx->state = RP_COMMON_STATE_IDLE;
1057-
1058-
/* Mark the connection for termination */
1059-
conn->llcp_terminate.reason_final = ctx->data.term.error_code;
1067+
#if defined(CONFIG_BT_CENTRAL)
1068+
if (conn->lll.role == BT_HCI_ROLE_CENTRAL) {
1069+
/* No response, but postpone terminate until next event
1070+
* to ensure acking the reception of TERMINATE_IND
1071+
*/
1072+
ctx->state = RP_COMMON_STATE_POSTPONE_TERMINATE;
1073+
break;
1074+
}
1075+
#endif
1076+
#if defined(CONFIG_BT_PERIPHERAL)
1077+
/* Terminate right away */
1078+
rp_comm_terminate(conn, ctx);
1079+
#endif
10601080
break;
10611081
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
10621082
case PROC_DATA_LENGTH_UPDATE:
@@ -1102,6 +1122,26 @@ static void rp_comm_st_wait_rx(struct ll_conn *conn, struct proc_ctx *ctx, uint8
11021122
}
11031123
}
11041124

1125+
static void rp_comm_st_postpone_terminate(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
1126+
void *param)
1127+
{
1128+
switch (evt) {
1129+
case RP_COMMON_EVT_RUN:
1130+
LL_ASSERT(ctx->proc == PROC_TERMINATE);
1131+
1132+
/* Note: now we terminate, mimicking legacy LLCP behaviour
1133+
* A check should be added to ensure that the ack of the terminate_ind was
1134+
* indeed tx'ed and not scheduled out/postponed by LLL
1135+
*/
1136+
rp_comm_terminate(conn, ctx);
1137+
1138+
break;
1139+
default:
1140+
/* Ignore other evts */
1141+
break;
1142+
}
1143+
}
1144+
11051145
static void rp_comm_st_wait_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
11061146
{
11071147
switch (evt) {
@@ -1181,6 +1221,9 @@ static void rp_comm_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint
11811221
case RP_COMMON_STATE_WAIT_RX:
11821222
rp_comm_st_wait_rx(conn, ctx, evt, param);
11831223
break;
1224+
case RP_COMMON_STATE_POSTPONE_TERMINATE:
1225+
rp_comm_st_postpone_terminate(conn, ctx, evt, param);
1226+
break;
11841227
case RP_COMMON_STATE_WAIT_TX:
11851228
rp_comm_st_wait_tx(conn, ctx, evt, param);
11861229
break;

subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ enum llcp_tx_q_pause_data_mask {
3333
LLCP_TX_QUEUE_PAUSE_DATA_ENCRYPTION = 0x01,
3434
LLCP_TX_QUEUE_PAUSE_DATA_PHY_UPDATE = 0x02,
3535
LLCP_TX_QUEUE_PAUSE_DATA_DATA_LENGTH = 0x04,
36+
LLCP_TX_QUEUE_PAUSE_DATA_TERMINATE = 0x08,
3637
};
3738

3839
#if ((CONFIG_BT_CTLR_LLCP_COMMON_TX_CTRL_BUF_NUM <\

tests/bluetooth/controller/ctrl_terminate/src/main.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,16 @@ static void test_terminate_rem(uint8_t role)
6666
/* Done */
6767
event_done(&conn);
6868

69+
/* Prepare */
70+
event_prepare(&conn);
71+
72+
/* Done */
73+
event_done(&conn);
74+
6975
/* There should be no host notification */
7076
ut_rx_q_is_empty();
7177

78+
7279
zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(),
7380
"Free CTX buffers %d", ctx_buffers_free());
7481
}

0 commit comments

Comments
 (0)