Bluetooth: Controller: Fix instant based procedure complete event

Fix instant based procedure complete event generation to be
held until after the on-air instant has elapsed.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
diff --git a/subsys/bluetooth/controller/Kconfig.ll_sw_split b/subsys/bluetooth/controller/Kconfig.ll_sw_split
index c84f98c..b50144c 100644
--- a/subsys/bluetooth/controller/Kconfig.ll_sw_split
+++ b/subsys/bluetooth/controller/Kconfig.ll_sw_split
@@ -563,6 +563,7 @@
 
 config BT_CTLR_RX_ENQUEUE_HOLD
 	bool "Procedure Complete after on-air instant"
+	depends on BT_LL_SW_LLCP_LEGACY
 	default y if BT_HCI_RAW
 	help
 	  Hold enqueue of Procedure Complete events with instant until after the
diff --git a/subsys/bluetooth/controller/ll_sw/lll_conn.h b/subsys/bluetooth/controller/ll_sw/lll_conn.h
index 5c6874c..74b4735 100644
--- a/subsys/bluetooth/controller/ll_sw/lll_conn.h
+++ b/subsys/bluetooth/controller/ll_sw/lll_conn.h
@@ -140,6 +140,14 @@
 #endif /* CONFIG_BT_CTLR_CONN_RSSI_EVENT */
 #endif /* CONFIG_BT_CTLR_CONN_RSSI */
 
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+#define RX_HOLD_MASK 3U
+#define RX_HOLD_REQ  1U
+#define RX_HOLD_ACK  2U
+	uint8_t rx_hold_req;
+	uint8_t rx_hold_ack;
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+
 #if defined(CONFIG_BT_CTLR_CONN_META)
 	struct lll_conn_meta conn_meta;
 #endif /* CONFIG_BT_CTLR_CONN_META */
diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c
index c0db5dc..02eaa45 100644
--- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c
+++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c
@@ -395,6 +395,13 @@
 
 	is_ull_rx = 0U;
 
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+	if (((lll->rx_hold_req - lll->rx_hold_ack) & RX_HOLD_MASK) ==
+	    RX_HOLD_REQ) {
+		lll->rx_hold_ack--;
+	}
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+
 	if (tx_release) {
 		LL_ASSERT(lll->handle != 0xFFFF);
 
diff --git a/subsys/bluetooth/controller/ll_sw/ull_adv.c b/subsys/bluetooth/controller/ll_sw/ull_adv.c
index e0cf99c..042c9b8 100644
--- a/subsys/bluetooth/controller/ll_sw/ull_adv.c
+++ b/subsys/bluetooth/controller/ll_sw/ull_adv.c
@@ -1092,6 +1092,12 @@
 		 */
 		conn->llcp_terminate.node_rx.hdr.link = link;
 
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+		conn->llcp_rx_hold = NULL;
+		conn_lll->rx_hold_req = 0U;
+		conn_lll->rx_hold_ack = 0U;
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+
 #if defined(CONFIG_BT_CTLR_LE_ENC)
 		conn_lll->enc_rx = conn_lll->enc_tx = 0U;
 		conn->llcp_enc.req = conn->llcp_enc.ack = 0U;
diff --git a/subsys/bluetooth/controller/ll_sw/ull_central.c b/subsys/bluetooth/controller/ll_sw/ull_central.c
index 28b8960..e2dc5d1 100644
--- a/subsys/bluetooth/controller/ll_sw/ull_central.c
+++ b/subsys/bluetooth/controller/ll_sw/ull_central.c
@@ -325,6 +325,12 @@
 	 */
 	conn->llcp_terminate.node_rx.hdr.link = link;
 
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+	conn->llcp_rx_hold = NULL;
+	conn_lll->rx_hold_req = 0U;
+	conn_lll->rx_hold_ack = 0U;
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+
 #if defined(CONFIG_BT_CTLR_LE_ENC)
 	conn_lll->enc_rx = conn_lll->enc_tx = 0U;
 	conn->llcp_enc.req = conn->llcp_enc.ack = 0U;
diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn.c b/subsys/bluetooth/controller/ll_sw/ull_conn.c
index 0fec916..475a213 100644
--- a/subsys/bluetooth/controller/ll_sw/ull_conn.c
+++ b/subsys/bluetooth/controller/ll_sw/ull_conn.c
@@ -73,6 +73,10 @@
 #include "hal/debug.h"
 
 static int init_reset(void);
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+static bool rx_hold_is_done(struct ll_conn *conn);
+static void rx_hold_flush(struct ll_conn *conn);
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
 #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL)
 static void tx_demux_sched(struct ll_conn *conn);
 #endif /* CONFIG_BT_CTLR_LOW_LAT_ULL */
@@ -1010,6 +1014,12 @@
 		return 0;
 	}
 
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+	if (conn->llcp_rx_hold && rx_hold_is_done(conn)) {
+		rx_hold_flush(conn);
+	}
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+
 	pdu_rx = (void *)(*rx)->pdu;
 
 	switch (pdu_rx->ll_id) {
@@ -1379,6 +1389,16 @@
 		return;
 	}
 
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+	if (conn->llcp_rx_hold && rx_hold_is_done(conn)) {
+		rx_hold_flush(conn);
+
+#if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL)
+		ll_rx_sched();
+#endif /* !CONFIG_BT_CTLR_LOW_LAT_ULL */
+	}
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+
 #if defined(CONFIG_BT_CTLR_LE_ENC)
 	/* Check authenticated payload expiry or MIC failure */
 	switch (done->extra.mic_state) {
@@ -1936,6 +1956,12 @@
 		if (handle != LLL_HANDLE_INVALID) {
 			struct ll_conn *conn = ll_conn_get(handle);
 
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+			if (conn->llcp_rx_hold && rx_hold_is_done(conn)) {
+				rx_hold_flush(conn);
+			}
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+
 #if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
 			ctrl_tx_ack(conn, &tx, pdu_tx);
 #else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
@@ -1964,6 +1990,14 @@
 		pdu_tx->ll_id = PDU_DATA_LLID_RESV;
 	} else {
 		LL_ASSERT(handle != LLL_HANDLE_INVALID);
+
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+		struct ll_conn *conn = ll_conn_get(handle);
+
+		if (conn->llcp_rx_hold && rx_hold_is_done(conn)) {
+			rx_hold_flush(conn);
+		}
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
 	}
 
 	ll_tx_ack_put(handle, tx);
@@ -2154,6 +2188,72 @@
 	return 0;
 }
 
+#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
+static void rx_hold_put(struct ll_conn *conn, memq_link_t *link,
+			struct node_rx_pdu *rx)
+{
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+	struct node_rx_pdu *rx_last;
+	struct lll_conn *lll;
+
+	link->mem = NULL;
+	rx->hdr.link = link;
+
+	rx_last = conn->llcp_rx_hold;
+	while (rx_last && rx_last->hdr.link && rx_last->hdr.link->mem) {
+		rx_last = rx_last->hdr.link->mem;
+	}
+
+	if (rx_last) {
+		rx_last->hdr.link->mem = rx;
+	} else {
+		conn->llcp_rx_hold = rx;
+	}
+
+	lll = &conn->lll;
+	if (lll->rx_hold_req == lll->rx_hold_ack) {
+		lll->rx_hold_req++;
+	}
+
+#else /* !CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+	ARG_UNUSED(conn);
+
+	ll_rx_put(link, rx);
+#endif /* !CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+}
+
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+static bool rx_hold_is_done(struct ll_conn *conn)
+{
+	return ((conn->lll.rx_hold_req -
+		 conn->lll.rx_hold_ack) & RX_HOLD_MASK) == RX_HOLD_ACK;
+}
+
+static void rx_hold_flush(struct ll_conn *conn)
+{
+	struct node_rx_pdu *rx;
+	struct lll_conn *lll;
+
+	rx = conn->llcp_rx_hold;
+	do {
+		struct node_rx_hdr *hdr;
+
+		/* traverse to next rx node */
+		hdr = &rx->hdr;
+		rx = hdr->link->mem;
+
+		/* enqueue rx node towards Thread */
+		ll_rx_put(hdr->link, hdr);
+	} while (rx);
+
+	conn->llcp_rx_hold = NULL;
+	lll = &conn->lll;
+	lll->rx_hold_req = 0U;
+	lll->rx_hold_ack = 0U;
+}
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
+
 #if !defined(CONFIG_BT_CTLR_LOW_LAT_ULL)
 static void tx_demux_sched(struct ll_conn *conn)
 {
@@ -3121,14 +3221,21 @@
 			cu->interval = conn->llcp_cu.interval;
 			cu->latency = conn->llcp_cu.latency;
 			cu->timeout = conn->llcp_cu.timeout;
+
+			/* hold node rx until the instant's anchor point sync */
+			rx_hold_put(conn, rx->hdr.link, rx);
+
+			if (!IS_ENABLED(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)) {
+				ll_rx_sched();
+			}
 		} else {
 			/* Mark for buffer for release */
 			rx->hdr.type = NODE_RX_TYPE_RELEASE;
-		}
 
-		/* enqueue rx node towards Thread */
-		ll_rx_put(rx->hdr.link, rx);
-		ll_rx_sched();
+			/* enqueue rx node towards Thread */
+			ll_rx_put(rx->hdr.link, rx);
+			ll_rx_sched();
+		}
 
 #if defined(CONFIG_BT_CTLR_XTAL_ADVANCED)
 		/* restore to normal prepare */
@@ -4672,8 +4779,8 @@
 		upd->tx = lll->phy_tx;
 		upd->rx = lll->phy_rx;
 
-		/* enqueue rx node towards Thread */
-		ll_rx_put(rx->hdr.link, rx);
+		/* hold node rx until the instant's anchor point sync */
+		rx_hold_put(conn, rx->hdr.link, rx);
 
 #if defined(CONFIG_BT_CTLR_DATA_LENGTH)
 		/* get a rx node for ULL->LL */
@@ -4716,11 +4823,13 @@
 		lr->max_rx_time = sys_cpu_to_le16(lll->max_rx_time);
 		lr->max_tx_time = sys_cpu_to_le16(lll->max_tx_time);
 
-		/* enqueue rx node towards Thread */
-		ll_rx_put(rx->hdr.link, rx);
+		/* hold node rx until the instant's anchor point sync */
+		rx_hold_put(conn, rx->hdr.link, rx);
 #endif /* CONFIG_BT_CTLR_DATA_LENGTH */
 
-		ll_rx_sched();
+		if (!IS_ENABLED(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)) {
+			ll_rx_sched();
+		}
 	}
 }
 #endif /* CONFIG_BT_CTLR_PHY */
diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h
index 85cbafc..9bd5f7b 100644
--- a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h
+++ b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h
@@ -153,6 +153,10 @@
 
 	struct node_rx_pdu *llcp_rx;
 
+#if defined(CONFIG_BT_CTLR_RX_ENQUEUE_HOLD)
+	struct node_rx_pdu *llcp_rx_hold;
+#endif /* CONFIG_BT_CTLR_RX_ENQUEUE_HOLD */
+
 	struct {
 		uint8_t  req;
 		uint8_t  ack;