From 551b22e8e65b84844fdf4b6abe659b7f39a0db0b Mon Sep 17 00:00:00 2001 From: Lucas Mathias Balling Date: Fri, 4 Oct 2024 13:01:29 +0200 Subject: [PATCH 1/5] Bluetooth: Controller: Implement Periodic Sync Procedure Adding PDU flow for Periodic Sync Indication Signed-off-by: Lucas Mathias Balling --- subsys/bluetooth/controller/CMakeLists.txt | 8 +- .../bluetooth/controller/Kconfig.ll_sw_split | 4 +- subsys/bluetooth/controller/include/ll_feat.h | 16 +- subsys/bluetooth/controller/ll_sw/lll_sync.h | 6 +- subsys/bluetooth/controller/ll_sw/pdu.h | 21 + subsys/bluetooth/controller/ll_sw/ull_llcp.c | 230 +++++++ subsys/bluetooth/controller/ll_sw/ull_llcp.h | 22 + .../bluetooth/controller/ll_sw/ull_llcp_cc.c | 8 - .../controller/ll_sw/ull_llcp_common.c | 12 + .../controller/ll_sw/ull_llcp_features.h | 5 + .../controller/ll_sw/ull_llcp_internal.h | 44 ++ .../controller/ll_sw/ull_llcp_local.c | 10 + .../controller/ll_sw/ull_llcp_past.c | 569 ++++++++++++++++++ .../bluetooth/controller/ll_sw/ull_llcp_pdu.c | 79 +++ .../bluetooth/controller/ll_sw/ull_llcp_phy.c | 5 +- .../controller/ll_sw/ull_llcp_remote.c | 14 + 16 files changed, 1038 insertions(+), 15 deletions(-) create mode 100644 subsys/bluetooth/controller/ll_sw/ull_llcp_past.c diff --git a/subsys/bluetooth/controller/CMakeLists.txt b/subsys/bluetooth/controller/CMakeLists.txt index c9dae548f794b7..d5a990b52a05cb 100644 --- a/subsys/bluetooth/controller/CMakeLists.txt +++ b/subsys/bluetooth/controller/CMakeLists.txt @@ -98,7 +98,13 @@ if(CONFIG_BT_CONN) ll_sw/ull_llcp_chmu.c ll_sw/ull_llcp_remote.c ) - zephyr_library_sources_ifdef( + if (CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER OR + CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + zephyr_library_sources( + ll_sw/ull_llcp_past.c + ) + endif() + zephyr_library_sources_ifdef( CONFIG_BT_PERIPHERAL ll_sw/ull_peripheral.c ) diff --git a/subsys/bluetooth/controller/Kconfig.ll_sw_split b/subsys/bluetooth/controller/Kconfig.ll_sw_split index 9c326b35cf6f46..1970b4cd036760 100644 --- a/subsys/bluetooth/controller/Kconfig.ll_sw_split +++ b/subsys/bluetooth/controller/Kconfig.ll_sw_split @@ -48,6 +48,8 @@ config BT_LLL_VENDOR_NORDIC select BT_CTLR_CHAN_SEL_2_SUPPORT select BT_CTLR_MIN_USED_CHAN_SUPPORT select BT_CTLR_SCA_UPDATE_SUPPORT + select BT_CTLR_SYNC_TRANSFER_RECEIVER_SUPPORT + select BT_CTLR_SYNC_TRANSFER_SENDER_SUPPORT select BT_CTLR_DTM_HCI_SUPPORT select BT_CTLR_CONN_RSSI_SUPPORT @@ -61,7 +63,7 @@ config BT_LLL_VENDOR_NORDIC (BT_OBSERVER && BT_CTLR_ADV_EXT) select BT_TICKER_START_REMAINDER if BT_CTLR_CENTRAL_ISO select BT_TICKER_REMAINDER_GET if BT_BROADCASTER && BT_CTLR_ADV_EXT - select BT_TICKER_LAZY_GET if BT_CTLR_ADV_PERIODIC || BT_CTLR_CENTRAL_ISO + select BT_TICKER_LAZY_GET if BT_CTLR_ADV_PERIODIC || BT_CTLR_CENTRAL_ISO || BT_CTLR_SYNC_TRANSFER_SENDER select BT_TICKER_PREFER_START_BEFORE_STOP if BT_TICKER_SLOT_AGNOSTIC diff --git a/subsys/bluetooth/controller/include/ll_feat.h b/subsys/bluetooth/controller/include/ll_feat.h index 15e4eacd966d89..946441720769fe 100644 --- a/subsys/bluetooth/controller/include/ll_feat.h +++ b/subsys/bluetooth/controller/include/ll_feat.h @@ -177,6 +177,18 @@ #define LL_FEAT_BIT_SCA_UPDATE 0 #endif /* !CONFIG_BT_CTLR_SCA_UPDATE */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +#define LL_FEAT_BIT_SYNC_TRANSFER_SENDER BIT64(BT_LE_FEAT_BIT_PAST_SEND) +#else /* !CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ +#define LL_FEAT_BIT_SYNC_TRANSFER_SENDER 0 +#endif /* !CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +#define LL_FEAT_BIT_SYNC_TRANSFER_RECEIVER BIT64(BT_LE_FEAT_BIT_PAST_RECV) +#else /* !CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ +#define LL_FEAT_BIT_SYNC_TRANSFER_RECEIVER 0 +#endif /* !CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) #define LL_FEAT_BIT_CIS_CENTRAL BIT64(BT_LE_FEAT_BIT_CIS_CENTRAL) #else /* !CONFIG_BT_CTLR_CENTRAL_ISO */ @@ -282,7 +294,9 @@ LL_FEAT_BIT_CIS_PERIPHERAL | \ LL_FEAT_BIT_ISO_BROADCASTER | \ LL_FEAT_BIT_SYNC_RECEIVER | \ - LL_FEAT_BIT_PERIODIC_ADI_SUPPORT) + LL_FEAT_BIT_PERIODIC_ADI_SUPPORT | \ + LL_FEAT_BIT_SYNC_TRANSFER_RECEIVER | \ + LL_FEAT_BIT_SYNC_TRANSFER_SENDER) /* Connected Isochronous Stream (Host Support) bit is controlled by host */ #if defined(CONFIG_BT_CTLR_CONN_ISO) diff --git a/subsys/bluetooth/controller/ll_sw/lll_sync.h b/subsys/bluetooth/controller/ll_sw/lll_sync.h index 595c85396f0978..0bce7bd2260ffa 100644 --- a/subsys/bluetooth/controller/ll_sw/lll_sync.h +++ b/subsys/bluetooth/controller/ll_sw/lll_sync.h @@ -29,9 +29,11 @@ struct lll_sync { uint8_t is_aux_sched:1; uint8_t forced:1; -#if defined(CONFIG_BT_CTLR_SYNC_ISO) +#if defined(CONFIG_BT_CTLR_SYNC_ISO) || \ + defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) || \ + defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) uint8_t sca:3; -#endif /* CONFIG_BT_CTLR_SYNC_ISO */ +#endif /* CONFIG_BT_CTLR_SYNC_ISO || CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER/SENDER */ #if defined(CONFIG_BT_CTLR_SCAN_AUX_SYNC_RESERVE_MIN) /* Counter used by LLL abort of event when in unreserved time space to diff --git a/subsys/bluetooth/controller/ll_sw/pdu.h b/subsys/bluetooth/controller/ll_sw/pdu.h index 18ae111d8ae276..9457511977357f 100644 --- a/subsys/bluetooth/controller/ll_sw/pdu.h +++ b/subsys/bluetooth/controller/ll_sw/pdu.h @@ -614,6 +614,7 @@ enum pdu_data_llctrl_type { PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND = 0x19, PDU_DATA_LLCTRL_TYPE_CTE_REQ = 0x1A, PDU_DATA_LLCTRL_TYPE_CTE_RSP = 0x1B, + PDU_DATA_LLCTRL_TYPE_PERIODIC_SYNC_IND = 0x1C, PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ = 0x1D, PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP = 0x1E, PDU_DATA_LLCTRL_TYPE_CIS_REQ = 0x1F, @@ -888,6 +889,25 @@ struct pdu_data_llctrl_cis_terminate_ind { uint8_t error_code; } __packed; +struct pdu_data_llctrl_periodic_sync_ind { + uint16_t id; + struct pdu_adv_sync_info sync_info; + uint16_t conn_event_count; + uint16_t last_pa_event_counter; +#ifdef CONFIG_LITTLE_ENDIAN + uint8_t sid:4; + uint8_t addr_type:1; + uint8_t sca:3; +#else + uint8_t sca:3; + uint8_t addr_type:1; + uint8_t sid:4; +#endif /* CONFIG_LITTLE_ENDIAN */ + uint8_t phy; + uint8_t adv_addr[6]; + uint16_t sync_conn_event_count; +} __packed; + struct pdu_data_llctrl { uint8_t opcode; union { @@ -925,6 +945,7 @@ struct pdu_data_llctrl { struct pdu_data_llctrl_cis_rsp cis_rsp; struct pdu_data_llctrl_cis_ind cis_ind; struct pdu_data_llctrl_cis_terminate_ind cis_terminate_ind; + struct pdu_data_llctrl_periodic_sync_ind periodic_sync_ind; } __packed; } __packed; diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.c b/subsys/bluetooth/controller/ll_sw/ull_llcp.c index d194e2c75c76ff..1540f043cb301e 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.c @@ -18,6 +18,7 @@ #include "util/mem.h" #include "util/memq.h" #include "util/dbuf.h" +#include "util/mayfly.h" #include "pdu_df.h" #include "lll/pdu_vendor.h" @@ -32,11 +33,21 @@ #include "lll/lll_df_types.h" #include "lll_conn.h" #include "lll_conn_iso.h" +#include "lll_sync.h" +#include "lll_sync_iso.h" +#include "lll_scan.h" +#include "lll/lll_adv_types.h" +#include "lll_adv.h" +#include "lll/lll_adv_pdu.h" #include "ull_tx_queue.h" #include "isoal.h" #include "ull_iso_types.h" +#include "ull_sync_types.h" +#include "ull_scan_types.h" +#include "ull_adv_types.h" +#include "ull_adv_internal.h" #include "ull_conn_iso_types.h" #include "ull_conn_iso_internal.h" #include "ull_central_iso_internal.h" @@ -48,6 +59,9 @@ #include "ull_llcp_features.h" #include "ull_llcp_internal.h" #include "ull_peripheral_internal.h" +#include "ull_sync_internal.h" + +#include "ull_filter.h" #include #include "hal/debug.h" @@ -56,6 +70,7 @@ #define PROC_CTX_BUF_SIZE WB_UP(sizeof(struct proc_ctx)) #define TX_CTRL_BUF_SIZE WB_UP(offsetof(struct node_tx, pdu) + LLCTRL_PDU_SIZE) #define NTF_BUF_SIZE WB_UP(offsetof(struct node_rx_pdu, pdu) + LLCTRL_PDU_SIZE) +#define OFFSET_BASE_MAX_VALUE 0x1FFF /* LLCP Allocations */ #if defined(LLCP_TX_CTRL_BUF_QUEUE_ENABLE) @@ -1019,6 +1034,114 @@ uint8_t ull_cp_conn_update(struct ll_conn *conn, uint16_t interval_min, uint16_t return BT_HCI_ERR_SUCCESS; } +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +uint8_t ull_cp_periodic_sync(struct ll_conn *conn, struct ll_sync_set *sync, + struct ll_adv_sync_set *adv_sync, uint16_t service_data) +{ + struct pdu_adv_sync_info *si; + struct proc_ctx *ctx; + uint8_t *access_addr; + const uint8_t *adva; + uint16_t interval; + uint8_t *chan_map; + uint8_t *crc_init; + uint8_t addr_type; + uint8_t si_sca; + uint8_t sid; + uint8_t phy; + + /* Exactly one of the sync and adv_sync pointers should be non-null */ + LL_ASSERT((!adv_sync && sync) || (adv_sync && !sync)); + + if (!feature_peer_periodic_sync_recv(conn)) { + return BT_HCI_ERR_UNSUPP_REMOTE_FEATURE; + } + + ctx = llcp_create_local_procedure(PROC_PERIODIC_SYNC); + if (!ctx) { + return BT_HCI_ERR_CMD_DISALLOWED; + } + + if (sync) { + chan_map = sync->lll.chm[sync->lll.chm_first].data_chan_map; + si_sca = sync->lll.sca; + access_addr = sync->lll.access_addr; + crc_init = sync->lll.crc_init; + sid = sync->sid; + phy = sync->lll.phy; + interval = sync->interval; + + addr_type = sync->peer_id_addr_type; + if (sync->peer_addr_resolved) { + uint8_t rl_idx; + + /* peer_id_addr contains the identity address; Get the peers RPA */ + + rl_idx = ull_filter_rl_find(addr_type, sync->peer_id_addr, NULL); + + /* A resolved address must be present in the resolve list */ + LL_ASSERT(rl_idx < ll_rl_size_get()); + + /* Generate RPAs if required */ + ull_filter_rpa_update(false); + + /* Note: Since we need the peers RPA, use tgta_get */ + adva = ull_filter_tgta_get(rl_idx); + } else { + adva = sync->peer_id_addr; + } + } else { + struct ll_adv_set *adv; + struct pdu_adv *adv_pdu; + + chan_map = adv_sync->lll.chm[adv_sync->lll.chm_first].data_chan_map; + si_sca = lll_clock_sca_local_get(); + access_addr = adv_sync->lll.access_addr; + crc_init = adv_sync->lll.crc_init; + phy = adv_sync->lll.adv->phy_s; + interval = adv_sync->interval; + + adv = HDR_LLL2ULL(adv_sync->lll.adv); + sid = adv->sid; + + /* Pull AdvA from pdu */ + adv_pdu = lll_adv_sync_data_curr_get(&adv_sync->lll); + addr_type = adv_pdu->tx_addr; + /* Note: AdvA is mandatory for AUX_SYNC_IND and at the start of the ext. header */ + adva = adv_pdu->adv_ext_ind.ext_hdr.data; + } + + /* Store parameters in corresponding procedure context */ + ctx->data.periodic_sync.sync_handle = sync ? ull_sync_handle_get(sync) : + BT_HCI_SYNC_HANDLE_INVALID; + ctx->data.periodic_sync.adv_handle = adv_sync ? ull_adv_sync_handle_get(adv_sync) : + BT_HCI_ADV_HANDLE_INVALID; + ctx->data.periodic_sync.id = service_data; + ctx->data.periodic_sync.sca = lll_clock_sca_local_get(); + + si = &ctx->data.periodic_sync.sync_info; + si->interval = sys_cpu_to_le16(interval); + (void)memcpy(si->sca_chm, chan_map, sizeof(ctx->data.periodic_sync.sync_info.sca_chm)); + si->sca_chm[PDU_SYNC_INFO_SCA_CHM_SCA_BYTE_OFFSET] &= ~PDU_SYNC_INFO_SCA_CHM_SCA_BIT_MASK; + si->sca_chm[PDU_SYNC_INFO_SCA_CHM_SCA_BYTE_OFFSET] |= ((si_sca << + PDU_SYNC_INFO_SCA_CHM_SCA_BIT_POS) & + PDU_SYNC_INFO_SCA_CHM_SCA_BIT_MASK); + (void)memcpy(si->aa, access_addr, sizeof(si->aa)); + (void)memcpy(si->crc_init, crc_init, sizeof(si->crc_init)); + + ctx->data.periodic_sync.addr_type = addr_type; + (void)memcpy(ctx->data.periodic_sync.adv_addr, adva, BDADDR_SIZE); + ctx->data.periodic_sync.sid = sid; + ctx->data.periodic_sync.phy = phy; + + /* All timing sensitive parameters will be determined and filled when Tx PDU is enqueued. */ + + llcp_lr_enqueue(conn, ctx); + + return BT_HCI_ERR_SUCCESS; +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + #if defined(CONFIG_BT_CTLR_DATA_LENGTH) uint8_t ull_cp_remote_dle_pending(struct ll_conn *conn) { @@ -1169,6 +1292,7 @@ void ull_cp_cte_req_set_disable(struct ll_conn *conn) } #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ +#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) void ull_cp_cc_offset_calc_reply(struct ll_conn *conn, uint32_t cis_offset_min, uint32_t cis_offset_max) { @@ -1182,6 +1306,7 @@ void ull_cp_cc_offset_calc_reply(struct ll_conn *conn, uint32_t cis_offset_min, llcp_lp_cc_offset_calc_reply(conn, ctx); } } +#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ #if defined(CONFIG_BT_PERIPHERAL) && defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) bool ull_cp_cc_awaiting_reply(struct ll_conn *conn) @@ -1322,6 +1447,93 @@ bool ull_lp_cc_is_enqueued(struct ll_conn *conn) } #endif /* defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_CTLR_CENTRAL_ISO) */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +void ull_lp_past_offset_get_calc_params(struct ll_conn *conn, + uint8_t *adv_sync_handle, uint16_t *sync_handle) +{ + const struct proc_ctx *ctx; + + ctx = llcp_lr_peek_proc(conn, PROC_PERIODIC_SYNC); + + if (ctx) { + *adv_sync_handle = ctx->data.periodic_sync.adv_handle; + *sync_handle = ctx->data.periodic_sync.sync_handle; + } else { + *adv_sync_handle = BT_HCI_ADV_HANDLE_INVALID; + *sync_handle = BT_HCI_SYNC_HANDLE_INVALID; + } +} + +void ull_lp_past_offset_calc_reply(struct ll_conn *conn, uint32_t offset_us, + uint16_t pa_event_counter, uint16_t last_pa_event_counter) +{ + struct proc_ctx *ctx; + uint16_t conn_event_offset = 0; + + ctx = llcp_lr_peek_proc(conn, PROC_PERIODIC_SYNC); + + if (ctx) { + /* Check if the offset_us will fit within the sync_info offset fields */ + uint32_t max_offset = OFFS_ADJUST_US + OFFSET_BASE_MAX_VALUE * OFFS_UNIT_300_US; + + if (offset_us > max_offset) { + /* The offset_us is larger than what the sync_info offset fields can hold, + * therefore it needs to be compensated with a change in the + * connection event count - conn_event_count + */ + uint32_t conn_interval_us = conn->lll.interval * CONN_INT_UNIT_US; + + conn_event_offset = DIV_ROUND_UP(offset_us - max_offset, conn_interval_us); + + /* Update offset_us */ + offset_us = offset_us - (conn_event_offset * conn_interval_us); + + ctx->data.periodic_sync.conn_event_count = ull_conn_event_counter(conn) + + conn_event_offset; + } + + llcp_pdu_fill_sync_info_offset(&ctx->data.periodic_sync.sync_info, offset_us); +#if defined(CONFIG_BT_PERIPHERAL) + /* Save the result for later use */ + ctx->data.periodic_sync.offset_us = offset_us; +#endif /* CONFIG_BT_PERIPHERAL */ + + ctx->data.periodic_sync.sync_conn_event_count = ull_conn_event_counter(conn); + ctx->data.periodic_sync.conn_event_count = ull_conn_event_counter(conn) + + conn_event_offset; + + ctx->data.periodic_sync.sync_info.evt_cntr = pa_event_counter; + + ctx->data.periodic_sync.last_pa_event_counter = last_pa_event_counter; + + llcp_lp_past_offset_calc_reply(conn, ctx); + } +} + +void ull_lp_past_conn_evt_done(struct ll_conn *conn, struct node_rx_event_done *done) +{ + struct proc_ctx *ctx; + + ctx = llcp_lr_peek_proc(conn, PROC_PERIODIC_SYNC); + if (ctx) { +#if defined(CONFIG_BT_PERIPHERAL) + if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL && done->extra.trx_cnt) { + uint32_t start_to_actual_us; + + start_to_actual_us = isoal_get_wrapped_time_us( + done->extra.drift.start_to_address_actual_us, + (-done->extra.drift.preamble_to_addr_us)); + + ctx->data.periodic_sync.conn_start_to_actual_us = start_to_actual_us; + } +#endif /* CONFIG_BT_PERIPHERAL */ + + ctx->data.periodic_sync.conn_evt_trx = done->extra.trx_cnt; + llcp_lp_past_conn_evt_done(conn, ctx); + } +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + static bool pdu_is_expected(struct pdu_data *pdu, struct proc_ctx *ctx) { return (ctx->rx_opcode == pdu->llctrl.opcode || ctx->rx_greedy); @@ -1543,6 +1755,13 @@ static bool pdu_validate_clock_accuracy_rsp(struct pdu_data *pdu) return VALIDATE_PDU_LEN(pdu, clock_accuracy_rsp); } +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +static bool pdu_validate_periodic_sync_ind(struct pdu_data *pdu) +{ + return VALIDATE_PDU_LEN(pdu, periodic_sync_ind); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + typedef bool (*pdu_param_validate_t)(struct pdu_data *pdu); struct pdu_validate { @@ -1615,6 +1834,9 @@ static const struct pdu_validate pdu_validate[] = { [PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ] = { pdu_validate_clock_accuracy_req }, #endif /* CONFIG_BT_CTLR_SCA_UPDATE */ [PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP] = { pdu_validate_clock_accuracy_rsp }, +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + [PDU_DATA_LLCTRL_TYPE_PERIODIC_SYNC_IND] = { pdu_validate_periodic_sync_ind }, +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ }; static bool pdu_is_valid(struct pdu_data *pdu) @@ -1840,3 +2062,11 @@ struct proc_ctx *llcp_create_procedure(enum llcp_proc proc) return create_procedure(proc, &mem_local_ctx); } #endif + +bool phy_valid(uint8_t phy) +{ + /* This is equivalent to: + * exactly one bit set, and no bit set is rfu's + */ + return (phy == PHY_1M || phy == PHY_2M || phy == PHY_CODED); +} diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.h b/subsys/bluetooth/controller/ll_sw/ull_llcp.h index d9b32dd5a992a4..675b0119759ac5 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.h @@ -274,3 +274,25 @@ void ull_cp_cte_rsp_enable(struct ll_conn *conn, bool enable, uint8_t max_cte_le */ uint8_t ull_cp_req_peer_sca(struct ll_conn *conn); #endif /* CONFIG_BT_CTLR_SCA_UPDATE */ + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +struct ll_adv_sync_set; +struct ll_sync_set; + +/** + * @brief Initiate a Periodic Advertising Sync Transfer Procedure. + */ +uint8_t ull_cp_periodic_sync(struct ll_conn *conn, struct ll_sync_set *sync, + struct ll_adv_sync_set *adv_sync, uint16_t service_data); + +void ull_lp_past_offset_get_calc_params(struct ll_conn *conn, + uint8_t *adv_sync_handle, uint16_t *sync_handle); +void ull_lp_past_offset_calc_reply(struct ll_conn *conn, uint32_t offset_us, + uint16_t pa_event_counter, uint16_t last_pa_event_counter); +void ull_lp_past_conn_evt_done(struct ll_conn *conn, struct node_rx_event_done *done); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + +/** + * @brief Validation of PHY, checking if exactly one bit set, and no bit set is rfu's + */ +bool phy_valid(uint8_t phy); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c index a0f9b057a10645..92e66dd8e35f13 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c @@ -299,14 +299,6 @@ static void rp_cc_state_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t } } -static inline bool phy_valid(uint8_t phy) -{ - /* This is equivalent to: - * exactly one bit set, and no bit set is rfu's - */ - return (phy == PHY_1M || phy == PHY_2M || phy == PHY_CODED); -} - static uint8_t rp_cc_check_phy(struct ll_conn *conn, struct proc_ctx *ctx, struct pdu_data *pdu) { diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c index 2931b116542a56..b0e28e124bdc10 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c @@ -18,6 +18,7 @@ #include "util/mem.h" #include "util/memq.h" #include "util/dbuf.h" +#include "util/mayfly.h" #include "pdu_df.h" #include "lll/pdu_vendor.h" @@ -31,12 +32,22 @@ #include "lll/lll_df_types.h" #include "lll_conn.h" #include "lll_conn_iso.h" +#include "lll_sync.h" +#include "lll_sync_iso.h" +#include "lll_scan.h" +#include "lll/lll_adv_types.h" +#include "lll_adv.h" +#include "lll/lll_adv_pdu.h" #include "ull_tx_queue.h" #include "isoal.h" #include "ull_iso_types.h" #include "ull_conn_iso_types.h" +#include "ull_sync_types.h" +#include "ull_scan_types.h" +#include "ull_adv_types.h" +#include "ull_adv_internal.h" #include "ull_iso_internal.h" #include "ull_conn_iso_internal.h" #include "ull_peripheral_iso_internal.h" @@ -46,6 +57,7 @@ #include "ull_llcp.h" #include "ull_conn_internal.h" #include "ull_internal.h" +#include "ull_sync_internal.h" #include "ull_llcp_features.h" #include "ull_llcp_internal.h" diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h index 75d92d5524ef7c..c06d4978f2c654 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_features.h @@ -184,6 +184,11 @@ static inline bool feature_iso_peripheral(struct ll_conn *conn) return LL_FEAT_BIT_CIS_PERIPHERAL != 0; } +static inline bool feature_peer_periodic_sync_recv(struct ll_conn *conn) +{ + return (conn->llcp.fex.features_peer & BIT64(BT_LE_FEAT_BIT_PAST_RECV)) != 0; +} + /* * The following features are not yet defined in KConfig and do * not have a bitfield defined in ll_feat.h diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h index 5744f9c57232e8..446d78aeadb730 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h @@ -29,6 +29,7 @@ enum llcp_proc { PROC_CIS_CREATE, PROC_CIS_TERMINATE, PROC_SCA_UPDATE, + PROC_PERIODIC_SYNC, /* A helper enum entry, to use in pause procedure context */ PROC_NONE = 0x0, }; @@ -317,6 +318,31 @@ struct proc_ctx { uint8_t error_code; } sca_update; #endif /* CONFIG_BT_CTLR_SCA_UPDATE */ + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) || defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) + struct { + struct pdu_adv_sync_info sync_info; +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) && defined(CONFIG_BT_PERIPHERAL) + uint32_t conn_start_to_actual_us; + uint32_t offset_us; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER && CONFIG_BT_PERIPHERAL */ + uint16_t id; + uint16_t sync_conn_event_count; + uint16_t sync_handle; + uint16_t conn_event_count; + uint16_t last_pa_event_counter; + uint8_t adv_handle; + uint8_t adv_addr[6]; + uint8_t sid; + uint8_t addr_type:1; + uint8_t addr_resolved:1; + uint8_t sca; + uint8_t phy; +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) + uint8_t conn_evt_trx; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + } periodic_sync; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER || CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ } data; struct { @@ -768,6 +794,24 @@ void llcp_pdu_encode_cis_req(struct proc_ctx *ctx, struct pdu_data *pdu); void llcp_pdu_encode_cis_ind(struct proc_ctx *ctx, struct pdu_data *pdu); void llcp_pdu_decode_cis_rsp(struct proc_ctx *ctx, struct pdu_data *pdu); + +/* + * Periodic Advertising Sync Transfers Procedure Helper + */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +void llcp_pdu_fill_sync_info_offset(struct pdu_adv_sync_info *si, uint32_t offset_us); +void llcp_pdu_encode_periodic_sync_ind(struct proc_ctx *ctx, struct pdu_data *pdu); +void llcp_lp_past_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param); +void llcp_lp_past_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx); +void llcp_lp_past_conn_evt_done(struct ll_conn *conn, struct proc_ctx *ctx); +void llcp_lp_past_offset_calc_reply(struct ll_conn *conn, struct proc_ctx *ctx); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +void llcp_pdu_decode_periodic_sync_ind(struct proc_ctx *ctx, struct pdu_data *pdu); +void llcp_rp_past_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param); +void llcp_rp_past_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + #ifdef ZTEST_UNITTEST bool llcp_lr_is_disconnected(struct ll_conn *conn); bool llcp_lr_is_idle(struct ll_conn *conn); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c index 6307b50e3326d1..8c97bcdfb5f7d2 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c @@ -364,6 +364,11 @@ void llcp_lr_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx * llcp_lp_comm_tx_ack(conn, ctx, tx); break; #endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) + case PROC_PERIODIC_SYNC: + llcp_lp_past_tx_ack(conn, ctx, tx); + break; +#endif /* defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) */ default: break; /* Ignore tx_ack */ @@ -463,6 +468,11 @@ static void lr_act_run(struct ll_conn *conn) llcp_lp_comm_run(conn, ctx, NULL); break; #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) + case PROC_PERIODIC_SYNC: + llcp_lp_past_run(conn, ctx, NULL); + break; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ default: /* Unknown procedure */ LL_ASSERT(0); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_past.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_past.c new file mode 100644 index 00000000000000..a1d3887d115ec1 --- /dev/null +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_past.c @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2024 Demant A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include "hal/ccm.h" +#include "hal/debug.h" + +#include "util/memq.h" +#include "util/util.h" + +#include "pdu_df.h" +#include "lll/pdu_vendor.h" +#include "pdu.h" +#include "lll.h" +#include "lll_conn.h" +#include "lll_filter.h" +#include "ull_tx_queue.h" +#include "ull_conn_types.h" +#include "ull_llcp_internal.h" +#include "isoal.h" +#include "ull_iso_types.h" +#include "lll_conn_iso.h" +#include "ull_conn_iso_types.h" +#include "lll_sync.h" +#include "lll_sync_iso.h" +#include "ull_sync_types.h" +#include "lll_scan.h" +#include "ull_scan_types.h" +#include "ull_llcp.h" +#include "ull_internal.h" +#include "ull_conn_internal.h" +#include "ull_sync_internal.h" + +#include "ll_settings.h" +#include "ll_feat.h" +#include "ull_llcp_features.h" + + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +/* LLCP Remote Procedure FSM states */ +enum { + RP_PAST_STATE_IDLE, + RP_PAST_STATE_WAIT_RX, + RP_PAST_STATE_WAIT_NEXT_EVT, +#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY) + RP_PAST_STATE_WAIT_RESOLVE_INTERFACE_AVAIL, + RP_PAST_STATE_WAIT_RESOLVE_COMPLETE, +#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */ +}; + +/* LLCP Remote Procedure PAST (receiver) FSM events */ +enum { + /* Procedure run */ + RP_PAST_EVT_RUN, + + /* IND received */ + RP_PAST_EVT_RX, + + /* RPA resolve completed */ + RP_PAST_EVT_RESOLVED, +}; + +#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY) +/* Active connection for RPA resolve */ +static struct ll_conn *rp_past_resolve_conn; +#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */ + +static uint8_t rp_check_phy(struct ll_conn *conn, struct proc_ctx *ctx, + struct pdu_data *pdu) +{ + if (!phy_valid(pdu->llctrl.periodic_sync_ind.phy)) { + /* zero, more than one or any rfu bit selected in either phy */ + return BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL; + } + +#if defined(CONFIG_BT_CTLR_PHY) + const uint8_t phy = pdu->llctrl.periodic_sync_ind.phy; + + if (((phy & PHY_2M) && !IS_ENABLED(CONFIG_BT_CTLR_PHY_2M)) || + ((phy & PHY_CODED) && !IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED))) { + /* Unsupported phy selected */ + return BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL; + } +#endif /* CONFIG_BT_CTLR_PHY */ + + return BT_HCI_ERR_SUCCESS; +} + +static void rp_past_complete(struct ll_conn *conn, struct proc_ctx *ctx) +{ + llcp_rr_complete(conn); + ctx->state = RP_PAST_STATE_IDLE; +} + +static void rp_past_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) +{ + switch (evt) { + case RP_PAST_EVT_RUN: + ctx->state = RP_PAST_STATE_WAIT_RX; + break; + default: + /* Ignore other evts */ + break; + } +} + +#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY) +static void rp_past_resolve_cb(void *param) +{ + uint8_t rl_idx = (uint8_t)(uint32_t)param; + struct ll_conn *conn = rp_past_resolve_conn; + struct proc_ctx *ctx; + uint8_t id_addr_type; + + /* Release resolving interface */ + rp_past_resolve_conn = NULL; + + ctx = llcp_rr_peek(conn); + if (!ctx) { + /* No context - possibly due to connection termination or + * other cause of procedure completion. + */ + return; + } + + if (ctx->state != RP_PAST_STATE_WAIT_RESOLVE_COMPLETE) { + /* Wrong state - possibly due to connection termination or + * other cause of procedure completion. + */ + return; + } + + /* If resolve failed then just continue in next event using the RPA */ + if (rl_idx != FILTER_IDX_NONE) { + const bt_addr_t *id_addr; + + id_addr = ull_filter_lll_id_addr_get(rl_idx, &id_addr_type); + if (id_addr) { + memcpy(&ctx->data.periodic_sync.adv_addr, &id_addr->val, sizeof(bt_addr_t)); + + ctx->data.periodic_sync.addr_type = id_addr_type; + ctx->data.periodic_sync.addr_resolved = 1U; + } + } + + /* Let sync creation continue in next event */ + ctx->state = RP_PAST_STATE_WAIT_NEXT_EVT; +} + +static bool rp_past_addr_resolve(struct ll_conn *conn, struct proc_ctx *ctx) +{ + bt_addr_t adv_addr; + + if (rp_past_resolve_conn) { + /* Resolve interface busy */ + return false; + } + + (void)memcpy(&adv_addr.val, ctx->data.periodic_sync.adv_addr, sizeof(bt_addr_t)); + rp_past_resolve_conn = conn; + + if (ull_filter_deferred_resolve(&adv_addr, rp_past_resolve_cb)) { + /* Resolve initiated - wait for callback */ + return true; + } + + /* Resolve interface busy (in ull_filter) */ + rp_past_resolve_conn = NULL; + return false; +} + +static void rp_past_st_wait_resolve_if(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case RP_PAST_EVT_RUN: + if (rp_past_addr_resolve(conn, ctx)) { + ctx->state = RP_PAST_STATE_WAIT_RESOLVE_COMPLETE; + } + break; + default: + /* Ignore other evts */ + break; + } +} +#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */ + +static void rp_past_st_wait_rx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + struct pdu_data *pdu = (struct pdu_data *)param; + + switch (evt) { + case RP_PAST_EVT_RX: + llcp_pdu_decode_periodic_sync_ind(ctx, pdu); + + /* Check PHY */ + if (rp_check_phy(conn, ctx, pdu) != BT_HCI_ERR_SUCCESS) { + /* Invalid PHY - ignore and silently complete */ + rp_past_complete(conn, ctx); + return; + } + + ctx->data.periodic_sync.addr_resolved = 0U; + + if (ctx->data.periodic_sync.addr_type == BT_ADDR_LE_RANDOM) { + /* TODO: For efficiency, check if address resolve is needed */ +#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY) + if (ull_filter_lll_rl_enabled()) { + if (rp_past_addr_resolve(conn, ctx)) { + ctx->state = RP_PAST_STATE_WAIT_RESOLVE_COMPLETE; + return; + } + ctx->state = RP_PAST_STATE_WAIT_RESOLVE_INTERFACE_AVAIL; + return; + } +#else + /* TODO: Not implemented - use RPA */ +#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */ + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + /* If sync_conn_event_count is this connection event, + * we have to wait for drift correction for this event to be applied - + * continue processing in the next conn event + */ + if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL && + ctx->data.periodic_sync.sync_conn_event_count == + ull_conn_event_counter(conn)) { + ctx->state = RP_PAST_STATE_WAIT_NEXT_EVT; + return; + } + } + + /* Hand over to ULL */ + ull_sync_transfer_received(conn, + ctx->data.periodic_sync.id, + &ctx->data.periodic_sync.sync_info, + ctx->data.periodic_sync.conn_event_count, + ctx->data.periodic_sync.last_pa_event_counter, + ctx->data.periodic_sync.sid, + ctx->data.periodic_sync.addr_type, + ctx->data.periodic_sync.sca, + ctx->data.periodic_sync.phy, + ctx->data.periodic_sync.adv_addr, + ctx->data.periodic_sync.sync_conn_event_count, + ctx->data.periodic_sync.addr_resolved); + rp_past_complete(conn, ctx); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void rp_past_st_wait_next_evt(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case RP_PAST_EVT_RUN: +#if defined(CONFIG_BT_PERIPHERAL) + /* If sync_conn_event_count is this connection event, + * we have to wait for drift correction for this event to be applied - + * continue processing in the next conn event + */ + if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL && + ctx->data.periodic_sync.sync_conn_event_count == ull_conn_event_counter(conn)) { + return; + } +#endif /* CONFIG_BT_PERIPHERAL */ + + /* Hand over to ULL */ + ull_sync_transfer_received(conn, + ctx->data.periodic_sync.id, + &ctx->data.periodic_sync.sync_info, + ctx->data.periodic_sync.conn_event_count, + ctx->data.periodic_sync.last_pa_event_counter, + ctx->data.periodic_sync.sid, + ctx->data.periodic_sync.addr_type, + ctx->data.periodic_sync.sca, + ctx->data.periodic_sync.phy, + ctx->data.periodic_sync.adv_addr, + ctx->data.periodic_sync.sync_conn_event_count, + ctx->data.periodic_sync.addr_resolved); + rp_past_complete(conn, ctx); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void rp_past_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (ctx->state) { + case RP_PAST_STATE_IDLE: + rp_past_st_idle(conn, ctx, evt, param); + break; + case RP_PAST_STATE_WAIT_RX: + rp_past_st_wait_rx(conn, ctx, evt, param); + break; + case RP_PAST_STATE_WAIT_NEXT_EVT: + rp_past_st_wait_next_evt(conn, ctx, evt, param); + break; +#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY) + case RP_PAST_STATE_WAIT_RESOLVE_INTERFACE_AVAIL: + rp_past_st_wait_resolve_if(conn, ctx, evt, param); + break; + case RP_PAST_STATE_WAIT_RESOLVE_COMPLETE: + /* Waiting for callback - do nothing */ + break; +#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */ + default: + /* Unknown state */ + LL_ASSERT(0); + break; + } +} + +void llcp_rp_past_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx) +{ + rp_past_execute_fsm(conn, ctx, RP_PAST_EVT_RX, rx->pdu); +} + +void llcp_rp_past_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param) +{ + rp_past_execute_fsm(conn, ctx, RP_PAST_EVT_RUN, param); +} + +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +/* LLCP Local Procedure FSM states */ +enum { + LP_PAST_STATE_IDLE, + LP_PAST_STATE_WAIT_TX_REQ, + LP_PAST_STATE_WAIT_OFFSET_CALC, + LP_PAST_STATE_WAIT_TX_ACK, + LP_PAST_STATE_WAIT_EVT_DONE, +}; + +/* LLCP Local Procedure PAST (sender) FSM events */ +enum { + /* Procedure run */ + LP_PAST_EVT_RUN, + + /* Offset calculation reply received */ + LP_PAST_EVT_OFFSET_CALC_REPLY, + + /* RX received in connection event */ + LP_PAST_EVT_RX_RECEIVED, + + /* RX not received in connection event */ + LP_PAST_EVT_NO_RX_RECEIVED, + + /* Ack received */ + LP_PAST_EVT_ACK, +}; + +static void lp_past_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param); + +static void lp_past_complete(struct ll_conn *conn, struct proc_ctx *ctx) +{ + llcp_lr_complete(conn); + ctx->state = LP_PAST_STATE_IDLE; +} + +static void lp_past_tx(struct ll_conn *conn, struct proc_ctx *ctx) +{ + struct node_tx *tx; + struct pdu_data *pdu; + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL) { + uint32_t offset_us; + + /* Correct offset can be calculated now that we know the event start time */ + offset_us = ctx->data.periodic_sync.offset_us - + ctx->data.periodic_sync.conn_start_to_actual_us; + + llcp_pdu_fill_sync_info_offset(&ctx->data.periodic_sync.sync_info, + offset_us); + } + } + + /* Allocate tx node */ + tx = llcp_tx_alloc(conn, ctx); + LL_ASSERT(tx); + + pdu = (struct pdu_data *)tx->pdu; + + /* Encode LL Control PDU */ + llcp_pdu_encode_periodic_sync_ind(ctx, pdu); + + /* Enqueue LL Control PDU towards LLL */ + llcp_tx_enqueue(conn, tx); + + /* Wait for TX Ack */ + ctx->state = LP_PAST_STATE_WAIT_TX_ACK; + ctx->node_ref.tx_ack = tx; +} + +void llcp_lp_past_offset_calc_reply(struct ll_conn *conn, struct proc_ctx *ctx) +{ + lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_OFFSET_CALC_REPLY, NULL); +} + +static void lp_past_offset_calc_req(struct ll_conn *conn, struct proc_ctx *ctx, + uint8_t evt, void *param) +{ + if (llcp_lr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { + ctx->state = LP_PAST_STATE_WAIT_TX_REQ; + } else { + /* Call ULL and wait for reply */ + ctx->state = LP_PAST_STATE_WAIT_OFFSET_CALC; + ull_conn_past_sender_offset_request(conn); + } +} + +static void lp_past_st_wait_tx_req(struct ll_conn *conn, struct proc_ctx *ctx, + uint8_t evt, void *param) +{ + switch (evt) { + case LP_PAST_EVT_RUN: + lp_past_offset_calc_req(conn, ctx, evt, param); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void lp_past_st_wait_offset_calc(struct ll_conn *conn, struct proc_ctx *ctx, + uint8_t evt, void *param) +{ + switch (evt) { + case LP_PAST_EVT_OFFSET_CALC_REPLY: + if (ull_ref_get(&conn->ull)) { + /* Connection event still ongoing, wait for done */ + ctx->state = LP_PAST_STATE_WAIT_EVT_DONE; + } else if (ctx->data.periodic_sync.conn_evt_trx) { + /* Connection event done with successful rx from peer */ + lp_past_tx(conn, ctx); + } else { + /* Reset state and try again in next connection event */ + ctx->state = LP_PAST_STATE_WAIT_TX_REQ; + } + break; + default: + /* Ignore other evts */ + break; + } +} + +void llcp_lp_past_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx) +{ + lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_ACK, tx->pdu); +} + +void llcp_lp_past_conn_evt_done(struct ll_conn *conn, struct proc_ctx *ctx) +{ + if (ctx->data.periodic_sync.conn_evt_trx != 0) { + lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_RX_RECEIVED, NULL); + } else { + lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_NO_RX_RECEIVED, NULL); + } +} + +static void lp_past_state_wait_evt_done(struct ll_conn *conn, struct proc_ctx *ctx, + uint8_t evt, void *param) +{ + /* Offset calculation has to be done in a connection event where a packet + * was received from the peer. + * From Core Spec v5.4, Vol 6, Part B, Section 2.4.2.27: + * syncConnEventCount shall be set to the connection event counter for the + * connection event that the sending device used in determining the contents + * of this PDU. This shall be a connection event where the sending device + * received a packet from the device it will send the LL_PERIODIC_SYNC_- + * IND PDU to and, if the sending device is the Peripheral on the piconet con- + * taining those two devices, it used the received packet to synchronize its + * anchor point + */ + switch (evt) { + case LP_PAST_EVT_RX_RECEIVED: + lp_past_tx(conn, ctx); + break; + case LP_PAST_EVT_NO_RX_RECEIVED: + /* Try again in next connection event */ + ctx->state = LP_PAST_STATE_WAIT_TX_REQ; + break; + } +} + +static void lp_past_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) +{ + switch (evt) { + case LP_PAST_EVT_RUN: + /* In case feature exchange completed after past was enqueued + * peer PAST receiver support should be confirmed + */ + if (feature_peer_periodic_sync_recv(conn)) { + lp_past_offset_calc_req(conn, ctx, evt, param); + } else { + /* Peer doesn't support PAST Receiver; HCI gives us no way to + * indicate this to the host so just silently complete the procedure + */ + lp_past_complete(conn, ctx); + } + break; + default: + /* Ignore other evts */ + break; + } +} + +static void lp_past_st_wait_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (evt) { + case LP_PAST_EVT_ACK: + /* Received Ack - All done now */ + lp_past_complete(conn, ctx); + break; + default: + /* Ignore other evts */ + break; + } +} + +static void lp_past_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, + void *param) +{ + switch (ctx->state) { + case LP_PAST_STATE_IDLE: + lp_past_st_idle(conn, ctx, evt, param); + break; + case LP_PAST_STATE_WAIT_TX_REQ: + lp_past_st_wait_tx_req(conn, ctx, evt, param); + break; + case LP_PAST_STATE_WAIT_OFFSET_CALC: + lp_past_st_wait_offset_calc(conn, ctx, evt, param); + break; + case LP_PAST_STATE_WAIT_TX_ACK: + lp_past_st_wait_tx_ack(conn, ctx, evt, param); + break; + case LP_PAST_STATE_WAIT_EVT_DONE: + lp_past_state_wait_evt_done(conn, ctx, evt, param); + break; + default: + /* Unknown state */ + LL_ASSERT(0); + break; + } +} + +void llcp_lp_past_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param) +{ + lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_RUN, param); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c index d91914ff392523..240f2ccfab736c 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c @@ -40,6 +40,12 @@ #include "ull_conn_iso_types.h" #include "ull_conn_iso_internal.h" +#include "lll/lll_adv_types.h" +#include "lll_adv.h" +#include "ull_adv_types.h" +#include "lll_sync.h" +#include "lll_sync_iso.h" +#include "ull_sync_types.h" #include "ull_conn_types.h" #include "ull_llcp.h" #include "ull_llcp_internal.h" @@ -986,3 +992,76 @@ void llcp_pdu_decode_clock_accuracy_rsp(struct proc_ctx *ctx, struct pdu_data *p ctx->data.sca_update.sca = p->sca; } #endif /* CONFIG_BT_CTLR_SCA_UPDATE */ + +/* + * Periodic Adv Sync Transfer Procedure Helpers + */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +void llcp_pdu_fill_sync_info_offset(struct pdu_adv_sync_info *si, uint32_t offset_us) +{ + uint8_t offset_adjust = 0U; + uint8_t offset_units = 0U; + + if (offset_us >= OFFS_ADJUST_US) { + offset_us -= OFFS_ADJUST_US; + offset_adjust = 1U; + } + + offset_us = offset_us / OFFS_UNIT_30_US; + if (!!(offset_us >> OFFS_UNIT_BITS)) { + offset_us = offset_us / (OFFS_UNIT_300_US / OFFS_UNIT_30_US); + offset_units = OFFS_UNIT_VALUE_300_US; + } + + /* Fill in the offset */ + PDU_ADV_SYNC_INFO_OFFS_SET(si, offset_us, offset_units, offset_adjust); +} + +void llcp_pdu_encode_periodic_sync_ind(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + struct pdu_data_llctrl_periodic_sync_ind *p = &pdu->llctrl.periodic_sync_ind; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = PDU_DATA_LLCTRL_LEN(periodic_sync_ind); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PERIODIC_SYNC_IND; + + p->id = sys_cpu_to_le16(ctx->data.periodic_sync.id); + + memcpy(&p->sync_info, &ctx->data.periodic_sync.sync_info, sizeof(struct pdu_adv_sync_info)); + + p->sync_info.evt_cntr = sys_cpu_to_le16(ctx->data.periodic_sync.sync_info.evt_cntr); + + p->conn_event_count = sys_cpu_to_le16(ctx->data.periodic_sync.conn_event_count); + p->last_pa_event_counter = sys_cpu_to_le16(ctx->data.periodic_sync.last_pa_event_counter); + p->sid = ctx->data.periodic_sync.sid; + p->addr_type = ctx->data.periodic_sync.addr_type; + p->sca = ctx->data.periodic_sync.sca; + p->phy = ctx->data.periodic_sync.phy; + + memcpy(&p->adv_addr, &ctx->data.periodic_sync.adv_addr, sizeof(bt_addr_t)); + + p->sync_conn_event_count = sys_cpu_to_le16(ctx->data.periodic_sync.sync_conn_event_count); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +void llcp_pdu_decode_periodic_sync_ind(struct proc_ctx *ctx, struct pdu_data *pdu) +{ + struct pdu_data_llctrl_periodic_sync_ind *p = &pdu->llctrl.periodic_sync_ind; + + ctx->data.periodic_sync.id = sys_le16_to_cpu(p->id); + + memcpy(&ctx->data.periodic_sync.sync_info, &p->sync_info, sizeof(struct pdu_adv_sync_info)); + + ctx->data.periodic_sync.conn_event_count = sys_le16_to_cpu(p->conn_event_count); + ctx->data.periodic_sync.last_pa_event_counter = sys_le16_to_cpu(p->last_pa_event_counter); + ctx->data.periodic_sync.sid = p->sid; + ctx->data.periodic_sync.addr_type = p->addr_type; + ctx->data.periodic_sync.sca = p->sca; + ctx->data.periodic_sync.phy = p->phy; + + memcpy(&ctx->data.periodic_sync.adv_addr, &p->adv_addr, sizeof(bt_addr_t)); + + ctx->data.periodic_sync.sync_conn_event_count = sys_le16_to_cpu(p->sync_conn_event_count); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c index 006938d9b2fc24..bee1b48ae1b2bc 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c @@ -190,7 +190,7 @@ static void pu_reset_timing_restrict(struct ll_conn *conn) } #if defined(CONFIG_BT_PERIPHERAL) -static inline bool phy_valid(uint8_t phy) +static inline bool phy_validation_check_phy_ind(uint8_t phy) { /* This is equivalent to: * maximum one bit set, and no bit set is rfu's @@ -203,7 +203,8 @@ static uint8_t pu_check_update_ind(struct ll_conn *conn, struct proc_ctx *ctx) uint8_t ret = 0; /* Check if either phy selected is invalid */ - if (!phy_valid(ctx->data.pu.c_to_p_phy) || !phy_valid(ctx->data.pu.p_to_c_phy)) { + if (!phy_validation_check_phy_ind(ctx->data.pu.c_to_p_phy) || + !phy_validation_check_phy_ind(ctx->data.pu.p_to_c_phy)) { /* more than one or any rfu bit selected in either phy */ ctx->data.pu.error = BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL; ret = 1; diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c index 94351132ddc643..185cad7911d1b5 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c @@ -95,6 +95,7 @@ static bool proc_with_instant(struct proc_ctx *ctx) case PROC_CIS_TERMINATE: case PROC_CIS_CREATE: case PROC_SCA_UPDATE: + case PROC_PERIODIC_SYNC: return 0U; case PROC_PHY_UPDATE: case PROC_CONN_UPDATE: @@ -311,6 +312,11 @@ void llcp_rr_rx(struct ll_conn *conn, struct proc_ctx *ctx, memq_link_t *link, llcp_rp_comm_rx(conn, ctx, rx); break; #endif /* CONFIG_BT_CTLR_SCA_UPDATE */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + case PROC_PERIODIC_SYNC: + llcp_rp_past_rx(conn, ctx, rx); + break; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -446,6 +452,11 @@ static void rr_act_run(struct ll_conn *conn) llcp_rp_comm_run(conn, ctx, NULL); break; #endif /* CONFIG_BT_CTLR_SCA_UPDATE */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + case PROC_PERIODIC_SYNC: + llcp_rp_past_run(conn, ctx, NULL); + break; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ default: /* Unknown procedure */ LL_ASSERT(0); @@ -896,6 +907,9 @@ static const struct proc_role new_proc_lut[] = { #if defined(CONFIG_BT_CTLR_SCA_UPDATE) [PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ] = { PROC_SCA_UPDATE, ACCEPT_ROLE_BOTH }, #endif /* CONFIG_BT_CTLR_SCA_UPDATE */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + [PDU_DATA_LLCTRL_TYPE_PERIODIC_SYNC_IND] = { PROC_PERIODIC_SYNC, ACCEPT_ROLE_BOTH }, +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ }; void llcp_rr_new(struct ll_conn *conn, memq_link_t *link, struct node_rx_pdu *rx, bool valid_pdu) From ce58a1827a03c2dd15c05721715b3fff552708df Mon Sep 17 00:00:00 2001 From: Lucas Mathias Balling Date: Fri, 4 Oct 2024 13:22:40 +0200 Subject: [PATCH 2/5] tests: Bluetooth: LLCP unittests for Periodic Sync Procedure Added LLCP PDU Periodic Sync Indication unittests Signed-off-by: Lucas Mathias Balling --- .../controller/common/include/helper_pdu.h | 6 + .../controller/common/src/helper_pdu.c | 70 ++ .../controller/common/src/helper_util.c | 4 + .../ctrl_isoal/src/isoal_test_common.c | 21 + .../ctrl_periodic_sync/CMakeLists.txt | 16 + .../controller/ctrl_periodic_sync/Kconfig | 31 + .../controller/ctrl_periodic_sync/prj.conf | 43 ++ .../controller/ctrl_periodic_sync/src/main.c | 656 ++++++++++++++++++ .../ctrl_periodic_sync/testcase.yaml | 8 + .../controller/mock_ctrl/CMakeLists.txt | 4 + .../mock_ctrl/include/hal/ticker_vendor_hal.h | 27 + .../mock_ctrl/include/lll/lll_adv_pdu.h | 6 + .../controller/mock_ctrl/src/isoal.c | 13 + .../controller/mock_ctrl/src/mayfly.c | 3 +- .../controller/mock_ctrl/src/ticker.c | 15 + .../bluetooth/controller/mock_ctrl/src/ull.c | 12 + .../controller/mock_ctrl/src/ull_adv_sync.c | 29 + .../controller/mock_ctrl/src/ull_filter.c | 46 ++ .../controller/mock_ctrl/src/ull_sync.c | 59 ++ tests/bluetooth/controller/uut/CMakeLists.txt | 6 + 20 files changed, 1074 insertions(+), 1 deletion(-) create mode 100644 tests/bluetooth/controller/ctrl_periodic_sync/CMakeLists.txt create mode 100644 tests/bluetooth/controller/ctrl_periodic_sync/Kconfig create mode 100644 tests/bluetooth/controller/ctrl_periodic_sync/prj.conf create mode 100644 tests/bluetooth/controller/ctrl_periodic_sync/src/main.c create mode 100644 tests/bluetooth/controller/ctrl_periodic_sync/testcase.yaml create mode 100644 tests/bluetooth/controller/mock_ctrl/src/isoal.c create mode 100644 tests/bluetooth/controller/mock_ctrl/src/ull_adv_sync.c create mode 100644 tests/bluetooth/controller/mock_ctrl/src/ull_filter.c create mode 100644 tests/bluetooth/controller/mock_ctrl/src/ull_sync.c diff --git a/tests/bluetooth/controller/common/include/helper_pdu.h b/tests/bluetooth/controller/common/include/helper_pdu.h index a43912cb2b70c1..1f9c80cafdc72f 100644 --- a/tests/bluetooth/controller/common/include/helper_pdu.h +++ b/tests/bluetooth/controller/common/include/helper_pdu.h @@ -62,6 +62,8 @@ void helper_pdu_encode_cis_terminate_ind(struct pdu_data *pdu, void *param); void helper_pdu_encode_sca_req(struct pdu_data *pdu, void *param); void helper_pdu_encode_sca_rsp(struct pdu_data *pdu, void *param); +void helper_pdu_encode_periodic_sync_ind(struct pdu_data *pdu, void *param); + void helper_pdu_verify_ping_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param); void helper_pdu_verify_ping_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param); @@ -155,6 +157,9 @@ void helper_pdu_verify_cis_terminate_ind(const char *file, uint32_t line, struct void helper_pdu_verify_sca_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param); void helper_pdu_verify_sca_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param); +void helper_pdu_verify_periodic_sync_ind(const char *file, uint32_t line, struct pdu_data *pdu, + void *param); + void helper_node_verify_peer_sca_update(const char *file, uint32_t line, struct node_rx_pdu *rx, void *param); @@ -193,6 +198,7 @@ enum helper_pdu_opcode { LL_CIS_RSP, LL_CIS_IND, LL_CIS_TERMINATE_IND, + LL_PERIODIC_SYNC_IND, LL_ZERO, }; diff --git a/tests/bluetooth/controller/common/src/helper_pdu.c b/tests/bluetooth/controller/common/src/helper_pdu.c index 7419de8fbce40c..315178dc2ba0cc 100644 --- a/tests/bluetooth/controller/common/src/helper_pdu.c +++ b/tests/bluetooth/controller/common/src/helper_pdu.c @@ -504,6 +504,33 @@ void helper_pdu_encode_sca_rsp(struct pdu_data *pdu, void *param) pdu->llctrl.clock_accuracy_rsp.sca = p->sca; } +void helper_pdu_encode_periodic_sync_ind(struct pdu_data *pdu, void *param) +{ + struct pdu_data_llctrl_periodic_sync_ind *p = param; + + pdu->ll_id = PDU_DATA_LLID_CTRL; + pdu->len = offsetof(struct pdu_data_llctrl, periodic_sync_ind) + + sizeof(struct pdu_data_llctrl_periodic_sync_ind); + pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PERIODIC_SYNC_IND; + + pdu->llctrl.periodic_sync_ind.id = sys_cpu_to_le16(p->id); + + memcpy(&pdu->llctrl.periodic_sync_ind.sync_info, &p->sync_info, + sizeof(struct pdu_adv_sync_info)); + + pdu->llctrl.periodic_sync_ind.conn_event_count = sys_cpu_to_le16(p->conn_event_count); + pdu->llctrl.periodic_sync_ind.last_pa_event_counter = + sys_cpu_to_le16(p->last_pa_event_counter); + pdu->llctrl.periodic_sync_ind.sid = p->sid; + pdu->llctrl.periodic_sync_ind.addr_type = p->addr_type; + pdu->llctrl.periodic_sync_ind.sca = p->sca; + pdu->llctrl.periodic_sync_ind.phy = p->phy; + + memcpy(pdu->llctrl.periodic_sync_ind.adv_addr, p->adv_addr, sizeof(p->adv_addr)); + + pdu->llctrl.periodic_sync_ind.sync_conn_event_count = p->sync_conn_event_count; +} + void helper_pdu_verify_version_ind(const char *file, uint32_t line, struct pdu_data *pdu, void *param) { @@ -1253,3 +1280,46 @@ void helper_pdu_verify_sca_rsp(const char *file, uint32_t line, struct pdu_data zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP, "Not a LL_CLOCK_ACCURACY_RSP.\nCalled at %s:%d\n", file, line); } + +void helper_pdu_verify_periodic_sync_ind(const char *file, uint32_t line, struct pdu_data *pdu, + void *param) +{ + struct pdu_data_llctrl_periodic_sync_ind *p = param; + + zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file, + line); + zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_PERIODIC_SYNC_IND, + "Not a LL_PERIODIC_SYNC_IND.\nCalled at %s:%d\n", file, line); + + zassert_equal(pdu->llctrl.periodic_sync_ind.id, p->id, + "id mismatch.\nCalled at %s:%d\n", file, line); + + zassert_equal(pdu->llctrl.periodic_sync_ind.conn_event_count, p->conn_event_count, + "conn_event_count mismatch.\nCalled at %s:%d\n", file, line); + + zassert_equal(pdu->llctrl.periodic_sync_ind.last_pa_event_counter, p->last_pa_event_counter, + "last_pa_event_counter mismatch.\nCalled at %s:%d\n", file, line); + + zassert_equal(pdu->llctrl.periodic_sync_ind.sid, p->sid, + "sid mismatch.\nCalled at %s:%d\n", file, line); + + zassert_equal(pdu->llctrl.periodic_sync_ind.addr_type, p->addr_type, + "addr_type mismatch.\nCalled at %s:%d\n", file, line); + + zassert_equal(pdu->llctrl.periodic_sync_ind.sca, p->sca, + "sca mismatch.\nCalled at %s:%d\n", file, line); + + zassert_equal(pdu->llctrl.periodic_sync_ind.phy, p->phy, + "phy mismatch.\nCalled at %s:%d\n", file, line); + + zassert_equal(pdu->llctrl.periodic_sync_ind.sync_conn_event_count, p->sync_conn_event_count, + "sync_conn_event_count mismatch.\nCalled at %s:%d\n", file, line); + + zassert_mem_equal(&pdu->llctrl.periodic_sync_ind.sync_info, &p->sync_info, + sizeof(struct pdu_adv_sync_info), + "sync_info mismatch.\nCalled at %s:%d\n", file, line); + + zassert_mem_equal(pdu->llctrl.periodic_sync_ind.adv_addr, p->adv_addr, + sizeof(p->adv_addr), + "adv_addr mismatch.\nCalled at %s:%d\n", file, line); +} diff --git a/tests/bluetooth/controller/common/src/helper_util.c b/tests/bluetooth/controller/common/src/helper_util.c index 6d05630558daf8..5aeca45c90c8f1 100644 --- a/tests/bluetooth/controller/common/src/helper_util.c +++ b/tests/bluetooth/controller/common/src/helper_util.c @@ -96,6 +96,7 @@ helper_pdu_encode_func_t *const helper_pdu_encode[] = { [LL_CIS_RSP] = helper_pdu_encode_cis_rsp, [LL_CIS_IND] = helper_pdu_encode_cis_ind, [LL_CIS_TERMINATE_IND] = helper_pdu_encode_cis_terminate_ind, + [LL_PERIODIC_SYNC_IND] = helper_pdu_encode_periodic_sync_ind, [LL_ZERO] = helper_pdu_encode_zero, }; @@ -134,6 +135,7 @@ helper_pdu_verify_func_t *const helper_pdu_verify[] = { [LL_CIS_RSP] = helper_pdu_verify_cis_rsp, [LL_CIS_IND] = helper_pdu_verify_cis_ind, [LL_CIS_TERMINATE_IND] = helper_pdu_verify_cis_terminate_ind, + [LL_PERIODIC_SYNC_IND] = helper_pdu_verify_periodic_sync_ind, }; helper_pdu_ntf_verify_func_t *const helper_pdu_ntf_verify[] = { @@ -170,6 +172,7 @@ helper_pdu_ntf_verify_func_t *const helper_pdu_ntf_verify[] = { [LL_CIS_RSP] = NULL, [LL_CIS_IND] = NULL, [LL_CIS_TERMINATE_IND] = NULL, + [LL_PERIODIC_SYNC_IND] = NULL, }; helper_node_encode_func_t *const helper_node_encode[] = { @@ -203,6 +206,7 @@ helper_node_encode_func_t *const helper_node_encode[] = { [LL_CIS_RSP] = NULL, [LL_CIS_IND] = NULL, [LL_CIS_TERMINATE_IND] = NULL, + [LL_PERIODIC_SYNC_IND] = NULL, }; helper_node_verify_func_t *const helper_node_verify[] = { diff --git a/tests/bluetooth/controller/ctrl_isoal/src/isoal_test_common.c b/tests/bluetooth/controller/ctrl_isoal/src/isoal_test_common.c index 32536201a3a01c..ad7c744722b21c 100644 --- a/tests/bluetooth/controller/ctrl_isoal/src/isoal_test_common.c +++ b/tests/bluetooth/controller/ctrl_isoal/src/isoal_test_common.c @@ -20,6 +20,11 @@ #include #include +#include "hal/cpu.h" +#include "hal/ccm.h" +#include "hal/cntr.h" +#include "hal/ticker.h" + #include "util/memq.h" #include "pdu_df.h" @@ -35,6 +40,8 @@ #include "isoal_test_common.h" #include "isoal_test_debug.h" +#define ULL_TIME_WRAPPING_POINT_US (HAL_TICKER_TICKS_TO_US_64BIT(HAL_TICKER_CNTR_MASK)) +#define ULL_TIME_SPAN_FULL_US (ULL_TIME_WRAPPING_POINT_US + 1) /** * Intializes a RX PDU buffer @@ -270,3 +277,17 @@ void init_test_data_buffer(uint8_t *buf, uint16_t size) buf[i] = (uint8_t)(i & 0x00FF); } } + +/** + * @brief Wraps given time within the range of 0 to ULL_TIME_WRAPPING_POINT_US + * @param time_now Current time value + * @param time_diff Time difference (signed) + * @return Wrapped time after difference + */ +uint32_t ull_get_wrapped_time_us(uint32_t time_now_us, int32_t time_diff_us) +{ + uint32_t result = ((uint64_t)time_now_us + ULL_TIME_SPAN_FULL_US + time_diff_us) % + ((uint64_t)ULL_TIME_SPAN_FULL_US); + + return result; +} diff --git a/tests/bluetooth/controller/ctrl_periodic_sync/CMakeLists.txt b/tests/bluetooth/controller/ctrl_periodic_sync/CMakeLists.txt new file mode 100644 index 00000000000000..3711f99428628e --- /dev/null +++ b/tests/bluetooth/controller/ctrl_periodic_sync/CMakeLists.txt @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +project(bluetooth_ull_llcp_periodic_sync) +find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/controller/common common) +add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/controller/uut uut) + +target_link_libraries(testbinary PRIVATE uut common) + +target_sources(testbinary + PRIVATE + src/main.c +) diff --git a/tests/bluetooth/controller/ctrl_periodic_sync/Kconfig b/tests/bluetooth/controller/ctrl_periodic_sync/Kconfig new file mode 100644 index 00000000000000..656446cacf39f1 --- /dev/null +++ b/tests/bluetooth/controller/ctrl_periodic_sync/Kconfig @@ -0,0 +1,31 @@ +# Copyright (c) 2022 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +# some of the control procedures in the BT LL depend on +# the following configs been set + +config SOC_COMPATIBLE_NRF + default y + +config BT_CTLR_DATA_LEN_UPDATE_SUPPORT + default y + +config BT_CTLR_PHY_UPDATE_SUPPORT + default y + +config BT_CTLR_PHY_CODED_SUPPORT + default y + +config BT_CTLR_PHY_2M_SUPPORT + default y + +config ENTROPY_NRF_FORCE_ALT + default n + +config ENTROPY_NRF5_RNG + default n + +source "tests/bluetooth/controller/common/Kconfig" + +# Include Zephyr's Kconfig +source "Kconfig" diff --git a/tests/bluetooth/controller/ctrl_periodic_sync/prj.conf b/tests/bluetooth/controller/ctrl_periodic_sync/prj.conf new file mode 100644 index 00000000000000..91c4ef94c2fa9c --- /dev/null +++ b/tests/bluetooth/controller/ctrl_periodic_sync/prj.conf @@ -0,0 +1,43 @@ +CONFIG_ZTEST=y + +CONFIG_ASSERT=y +CONFIG_ASSERT_LEVEL=2 +CONFIG_ASSERT_VERBOSE=y + +CONFIG_BT=y +CONFIG_BT_HCI=y +CONFIG_BT_CTLR=y +CONFIG_BT_CTLR_PRIVACY=y +CONFIG_BT_LL_SW_SPLIT=y + +CONFIG_BT_LLL_VENDOR_NORDIC=y + +CONFIG_BT_CENTRAL=y +CONFIG_BT_PERIPHERAL=y + +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_SIZE=251 +CONFIG_BT_DATA_LEN_UPDATE=y +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 + +CONFIG_BT_ASSERT=y +CONFIG_BT_CTLR_ASSERT_HANDLER=y + +CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG=y +CONFIG_BT_CTLR_SCA_UPDATE=y +CONFIG_BT_SCA_UPDATE=y + +CONFIG_BT_PER_ADV=y +CONFIG_BT_PER_ADV_SYNC=y +CONFIG_BT_EXT_ADV=y +CONFIG_BT_CTLR_ADV_PERIODIC=y +CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT=n +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER=y +CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER=y +CONFIG_BT_PER_ADV_SYNC_MAX=2 +CONFIG_BT_CTLR_RL_SIZE=8 + +CONFIG_BT_PHY_UPDATE=y +CONFIG_BT_CTLR_PHY_2M=y +CONFIG_BT_CTLR_PHY_CODED=y diff --git a/tests/bluetooth/controller/ctrl_periodic_sync/src/main.c b/tests/bluetooth/controller/ctrl_periodic_sync/src/main.c new file mode 100644 index 00000000000000..2be31106cfdc7a --- /dev/null +++ b/tests/bluetooth/controller/ctrl_periodic_sync/src/main.c @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2024 Demant + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +DEFINE_FFF_GLOBALS; + +#include +#include +#include +#include +#include "hal/ccm.h" + +#include "util/util.h" +#include "util/mem.h" +#include "util/memq.h" +#include "util/mayfly.h" +#include "util/dbuf.h" + +#include "pdu_df.h" +#include "lll/pdu_vendor.h" +#include "pdu.h" +#include "ll.h" +#include "ll_settings.h" +#include "ll_feat.h" + +#include "lll.h" +#include "lll/lll_df_types.h" +#include "lll_conn.h" +#include "lll_conn_iso.h" +#include "lll/lll_adv_types.h" +#include "lll_adv.h" +#include "lll/lll_adv_pdu.h" +#include "lll_chan.h" +#include "lll_scan.h" +#include "lll_sync.h" +#include "lll_sync_iso.h" + +#include "ull_adv_types.h" +#include "ull_scan_types.h" +#include "ull_sync_types.h" + +#include "ull_tx_queue.h" + +#include "isoal.h" +#include "ull_iso_types.h" +#include "ull_conn_iso_types.h" + +#include "ull_conn_types.h" +#include "ull_llcp.h" +#include "ull_llcp_internal.h" +#include "ull_sync_internal.h" +#include "ull_internal.h" + +#include "helper_pdu.h" +#include "helper_util.h" + +static struct ll_conn conn; +static struct ll_sync_set *sync; +static struct ll_adv_sync_set *adv_sync; + +static void periodic_sync_setup(void *data) +{ + test_setup(&conn); +} + +/* Custom fakes for this test suite */ + +/* uint32_t mayfly_enqueue(uint8_t caller_id, uint8_t callee_id, + * uint8_t chain, struct mayfly *m); + */ +FAKE_VALUE_FUNC(uint32_t, mayfly_enqueue, uint8_t, uint8_t, uint8_t, struct mayfly *); + +uint32_t mayfly_enqueue_custom_fake(uint8_t caller_id, uint8_t callee_id, + uint8_t chain, struct mayfly *m) +{ + /* Only proceed if it is the right mayfly enqueue used for getting the offset */ + if (m->param == &conn && chain == 1U) { + /* Mock that mayfly has run and ull_lp_past_offset_calc_reply() + * is called with the found past offset + */ + ull_lp_past_offset_calc_reply(&conn, 0, 0, 0); + } + + return 0; +} + +/* void ull_sync_transfer_received(struct ll_conn *conn, uint16_t service_data, + * struct pdu_adv_sync_info *si, uint16_t conn_event_count, + * uint16_t last_pa_event_counter, uint8_t sid, + * uint8_t addr_type, uint8_t sca, uint8_t phy, + * uint8_t *adv_addr, uint16_t sync_conn_event_count, + * uint8_t addr_resolved); + */ +FAKE_VOID_FUNC(ull_sync_transfer_received, struct ll_conn *, uint16_t, + struct pdu_adv_sync_info *, + uint16_t, + uint16_t, + uint8_t, + uint8_t, + uint8_t, + uint8_t, + uint8_t *, + uint16_t, + uint8_t); + +void ull_sync_transfer_received_custom_fake(struct ll_conn *conn, uint16_t service_data, + struct pdu_adv_sync_info *si, uint16_t conn_event_count, + uint16_t last_pa_event_counter, uint8_t sid, + uint8_t addr_type, uint8_t sca, uint8_t phy, + uint8_t *adv_addr, uint16_t sync_conn_event_count, + uint8_t addr_resolved) +{ +} +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | Periodic Adv. Sync Transfer| | + * | Proc. | | + * |--------------------------->| | + * | | | + * | | LL_PERIODIC_SYNC_IND| + * | |------------------> | + * | | 'll_ack'| + * | | | + * |Periodic Adv. Sync Transfer | | + * | Proc. Complete | | + * |<---------------------------| | + * | | | + */ +ZTEST(periodic_sync_transfer, test_periodic_sync_transfer_loc) +{ + uint8_t err; + struct node_tx *tx; + struct proc_ctx *ctx; + uint16_t service_data = 0; + + adv_sync = NULL; + sync = ull_sync_is_enabled_get(0); + + struct pdu_data_llctrl_periodic_sync_ind local_periodic_sync_ind = { + .id = 0x00, + .conn_event_count = 0x00, + .last_pa_event_counter = 0x00, + .sid = 0x00, + .addr_type = 0x00, + .sca = 0x00, + .phy = 0x00, + .adv_addr = { 0, 0, 0, 0, 0, 0}, + .sync_conn_event_count = 0 + }; + + /* Reset and setup mayfly_enqueue_custom_fake */ + RESET_FAKE(mayfly_enqueue); + mayfly_enqueue_fake .custom_fake = mayfly_enqueue_custom_fake; + + /* Initialise sync_info */ + memset(&local_periodic_sync_ind.sync_info, 0, sizeof(struct pdu_adv_sync_info)); + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + conn.llcp.fex.features_peer |= BIT64(BT_LE_FEAT_BIT_PAST_RECV); + + /* Initiate a Periodic Adv. Sync Transfer Procedure */ + err = ull_cp_periodic_sync(&conn, sync, adv_sync, service_data); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Connection event done with successful rx from peer */ + ctx = llcp_lr_peek_proc(&conn, PROC_PERIODIC_SYNC); + if (ctx) { + ctx->data.periodic_sync.conn_start_to_actual_us = 0; + ctx->data.periodic_sync.conn_evt_trx = 1; + llcp_lp_past_conn_evt_done(&conn, ctx); + } + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_PERIODIC_SYNC_IND, &conn, &tx, &local_periodic_sync_ind); + lt_rx_q_is_empty(&conn); + + /* TX Ack */ + event_tx_ack(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d and ctx_buffers_cnt is %d", llcp_ctx_buffers_free(), + test_ctx_buffers_cnt()); +} + +ZTEST(periodic_sync_transfer, test_periodic_sync_transfer_loc_2) +{ + uint8_t err; + uint16_t service_data = 0; + + sync = ull_sync_is_enabled_get(0); + adv_sync = NULL; + + ull_cp_init(); + ull_tx_q_init(&conn.tx_q); + ull_llcp_init(&conn); + conn.llcp.fex.features_peer |= BIT64(BT_LE_FEAT_BIT_PAST_RECV); + + err = ull_cp_periodic_sync(&conn, sync, adv_sync, service_data); + + for (int i = 0U; i < CONFIG_BT_CTLR_LLCP_LOCAL_PROC_CTX_BUF_NUM; i++) { + zassert_equal(err, BT_HCI_ERR_SUCCESS); + err = ull_cp_periodic_sync(&conn, sync, adv_sync, service_data); + } + + zassert_not_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + zassert_equal(llcp_ctx_buffers_free(), + test_ctx_buffers_cnt() - CONFIG_BT_CTLR_LLCP_LOCAL_PROC_CTX_BUF_NUM, + "Free CTX buffers %d", llcp_ctx_buffers_free()); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_PERIODIC_SYNC_IND | + * | |<----------------------- | + * | | | + * | | | + * | | | + * | | | + */ +ZTEST(periodic_sync_transfer, test_periodic_sync_transfer_rem) +{ + struct pdu_data_llctrl_periodic_sync_ind remote_periodic_sync_ind = { + .id = 0x01, + .conn_event_count = 0x00, + .last_pa_event_counter = 0x00, + .sid = 0x00, + .addr_type = 0x01, + .sca = 0x00, + .phy = 0x01, + .adv_addr = { 0, 0, 0, 0, 0, 0}, + .sync_conn_event_count = 0 + }; + + /* Reset and setup fake functions */ + RESET_FAKE(ull_sync_transfer_received); + ull_sync_transfer_received_fake.custom_fake = ull_sync_transfer_received_custom_fake; + + RESET_FAKE(mayfly_enqueue); + mayfly_enqueue_fake .custom_fake = mayfly_enqueue_custom_fake; + + /* Initialise sync_info */ + memset(&remote_periodic_sync_ind.sync_info, 0, sizeof(struct pdu_adv_sync_info)); + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + conn.llcp.fex.features_peer |= BIT64(BT_LE_FEAT_BIT_PAST_RECV); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_PERIODIC_SYNC_IND, &conn, &remote_periodic_sync_ind); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d and ctx_buffers_cnt is %d", llcp_ctx_buffers_free(), + test_ctx_buffers_cnt()); + + + /* Verify that ull_sync_transfer_received was called, + * hence the phy invalidation mechanism works + */ + zassert_equal(ull_sync_transfer_received_fake.call_count, 1, + "ull_sync_transfer_received_fake.call_count is %d and expected: %d", + ull_sync_transfer_received_fake.call_count, 1); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_PERIODIC_SYNC_IND | + * | |<------------------------| + * | | | + * | | | + * | | | + * | | | + * | Start | | + * | Periodic Adv. Sync Transfer| | + * | Proc. | | + * |--------------------------->| | + * | | | + * | | | + * | | LL_PERIODIC_SYNC_IND | + * | |------------------------>| + * | | 'll_ack'| + * |Periodic Adv. Sync Transfer | | + * | Proc. Complete | | + * |<---------------------------| | + * | | | + */ +ZTEST(periodic_sync_transfer, test_periodic_sync_transfer_rem_2) +{ + uint8_t err; + struct node_tx *tx; + uint16_t service_data = 0; + struct proc_ctx *ctx; + + sync = ull_sync_is_enabled_get(0); + adv_sync = NULL; + + struct pdu_data_llctrl_periodic_sync_ind local_periodic_sync_ind = { + .id = 0x00, + .conn_event_count = 0x01, + .last_pa_event_counter = 0x00, + .sid = 0x00, + .addr_type = 0x00, + .sca = 0x00, + .phy = 0x00, + .adv_addr = { 0, 0, 0, 0, 0, 0}, + .sync_conn_event_count = 0x01 + }; + + struct pdu_data_llctrl_periodic_sync_ind remote_periodic_sync_ind = { + .id = 0x01, + .conn_event_count = 0x00, + .last_pa_event_counter = 0x00, + .sid = 0x00, + .addr_type = 0x01, + .sca = 0x00, + .phy = 0x01, + .adv_addr = { 0, 0, 0, 0, 0, 0}, + .sync_conn_event_count = 0 + }; + + /* Reset and setup fake functions */ + RESET_FAKE(ull_sync_transfer_received); + ull_sync_transfer_received_fake.custom_fake = ull_sync_transfer_received_custom_fake; + + RESET_FAKE(mayfly_enqueue); + mayfly_enqueue_fake .custom_fake = mayfly_enqueue_custom_fake; + + /* Initialise sync_info */ + memset(&local_periodic_sync_ind.sync_info, 0, sizeof(struct pdu_adv_sync_info)); + memset(&remote_periodic_sync_ind.sync_info, 0, sizeof(struct pdu_adv_sync_info)); + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + conn.llcp.fex.features_peer |= BIT64(BT_LE_FEAT_BIT_PAST_RECV); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_PERIODIC_SYNC_IND, &conn, &remote_periodic_sync_ind); + + /* Done */ + event_done(&conn); + + /* Initiate a Periodic Adv. Sync Transfer Procedure */ + err = ull_cp_periodic_sync(&conn, sync, adv_sync, service_data); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Connection event done with successful rx from peer */ + ctx = llcp_lr_peek_proc(&conn, PROC_PERIODIC_SYNC); + if (ctx) { + ctx->data.periodic_sync.conn_start_to_actual_us = 0; + ctx->data.periodic_sync.conn_evt_trx = 1; + llcp_lp_past_conn_evt_done(&conn, ctx); + } + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_PERIODIC_SYNC_IND, &conn, &tx, &local_periodic_sync_ind); + lt_rx_q_is_empty(&conn); + + /* TX Ack */ + event_tx_ack(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d and ctx_buffers_cnt is %d", llcp_ctx_buffers_free(), + test_ctx_buffers_cnt()); + + /* Verify that ull_sync_transfer_received was called, + * hence the phy invalidation mechanism works + */ + zassert_equal(ull_sync_transfer_received_fake.call_count, 1, + "ull_sync_transfer_received_fake.call_count is %d and expected: %d", + ull_sync_transfer_received_fake.call_count, 1); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | Periodic Adv. Sync Transfer| | + * | Proc. | | + * |--------------------------->| | + * | | | + * | | LL_PERIODIC_SYNC_IND| + * | |------------------> | + * | | 'll_ack'| + * | | | + * | | | + * | | | + * |Periodic Adv. Sync Transfer | | + * |Proc. Complete | | + * |<---------------------------| | + * | Start | | + * | Periodic Adv. Sync Transfer| | + * | Proc. | | + * |--------------------------->| | + * | | | + * | | | + * | | LL_PERIODIC_SYNC_IND| + * | |------------------> | + * |Periodic Adv. Sync Transfer | 'll_ack'| + * | Proc. Complete | | + * |<---------------------------| | + * | | | + */ +ZTEST(periodic_sync_transfer, test_periodic_sync_transfer_loc_twice) +{ + uint8_t err; + struct node_tx *tx; + uint16_t service_data = 0; + struct proc_ctx *ctx; + + sync = ull_sync_is_enabled_get(0); + adv_sync = NULL; + + struct pdu_data_llctrl_periodic_sync_ind local_periodic_sync_ind = { + .id = 0x00, + .conn_event_count = 0x00, + .last_pa_event_counter = 0x00, + .sid = 0x00, + .addr_type = 0x00, + .sca = 0x00, + .phy = 0x00, + .adv_addr = { 0, 0, 0, 0, 0, 0}, + .sync_conn_event_count = 0 + }; + + /* Reset and setup mayfly_enqueue_custom_fake */ + RESET_FAKE(mayfly_enqueue); + mayfly_enqueue_fake .custom_fake = mayfly_enqueue_custom_fake; + + /* Initialise sync_info */ + memset(&local_periodic_sync_ind.sync_info, 0, sizeof(struct pdu_adv_sync_info)); + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + conn.llcp.fex.features_peer |= BIT64(BT_LE_FEAT_BIT_PAST_RECV); + + /* Initiate a periodic_sync Procedure */ + err = ull_cp_periodic_sync(&conn, sync, adv_sync, service_data); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Connection event done with successful rx from peer */ + ctx = llcp_lr_peek_proc(&conn, PROC_PERIODIC_SYNC); + if (ctx) { + ctx->data.periodic_sync.conn_start_to_actual_us = 0; + ctx->data.periodic_sync.conn_evt_trx = 1; + llcp_lp_past_conn_evt_done(&conn, ctx); + } + + /* Initiate a periodic_sync Procedure */ + err = ull_cp_periodic_sync(&conn, sync, adv_sync, service_data); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_PERIODIC_SYNC_IND, &conn, &tx, &local_periodic_sync_ind); + lt_rx_q_is_empty(&conn); + + /* TX Ack */ + event_tx_ack(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Increase connection event count */ + local_periodic_sync_ind.conn_event_count++; + local_periodic_sync_ind.sync_conn_event_count++; + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + /* Connection event done with successful rx from peer */ + ctx = llcp_lr_peek_proc(&conn, PROC_PERIODIC_SYNC); + if (ctx) { + ctx->data.periodic_sync.conn_start_to_actual_us = 0; + ctx->data.periodic_sync.conn_evt_trx = 1; + llcp_lp_past_conn_evt_done(&conn, ctx); + } + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_PERIODIC_SYNC_IND, &conn, &tx, &local_periodic_sync_ind); + lt_rx_q_is_empty(&conn); + + /* TX Ack */ + event_tx_ack(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + /* Second attempt to run the periodic_sync completes immediately in idle state. + * The context is released just after that. + */ + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d and ctx_buffers_cnt is %d", llcp_ctx_buffers_free(), + test_ctx_buffers_cnt()); +} + +/* + * +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_PERIODIC_SYNC_IND | + * | | (Invalid PHY) | + * | |<----------------------- | + * | | | + * | | | + * | | | + * | | | + */ +ZTEST(periodic_sync_transfer, test_periodic_sync_transfer_invalid_phy) +{ + struct pdu_data_llctrl_periodic_sync_ind remote_periodic_sync_ind = { + .id = 0x01, + .conn_event_count = 0x00, + .last_pa_event_counter = 0x00, + .sid = 0x00, + .addr_type = 0x01, + .sca = 0x00, + .phy = 0x03, + .adv_addr = { 0, 0, 0, 0, 0, 0}, + .sync_conn_event_count = 0 + }; + + /* Reset and setup ull_sync_transfer_received fake */ + RESET_FAKE(ull_sync_transfer_received); + ull_sync_transfer_received_fake.custom_fake = ull_sync_transfer_received_custom_fake; + + RESET_FAKE(mayfly_enqueue); + mayfly_enqueue_fake .custom_fake = mayfly_enqueue_custom_fake; + + /* Initialise sync_info */ + memset(&remote_periodic_sync_ind.sync_info, 0, sizeof(struct pdu_adv_sync_info)); + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + conn.llcp.fex.features_peer |= BIT64(BT_LE_FEAT_BIT_PAST_RECV); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_PERIODIC_SYNC_IND, &conn, &remote_periodic_sync_ind); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d and ctx_buffers_cnt is %d", llcp_ctx_buffers_free(), + test_ctx_buffers_cnt()); + + /* Verify that ull_sync_transfer_received was not called, + * hence the phy invalidation mechanism works + */ + zassert_equal(ull_sync_transfer_received_fake.call_count, 0, + "ull_sync_transfer_received_fake.call_count is %d and expected: %d", + ull_sync_transfer_received_fake.call_count, 0); +} + +ZTEST_SUITE(periodic_sync_transfer, NULL, NULL, periodic_sync_setup, NULL, NULL); diff --git a/tests/bluetooth/controller/ctrl_periodic_sync/testcase.yaml b/tests/bluetooth/controller/ctrl_periodic_sync/testcase.yaml new file mode 100644 index 00000000000000..e82df79b5bfbbb --- /dev/null +++ b/tests/bluetooth/controller/ctrl_periodic_sync/testcase.yaml @@ -0,0 +1,8 @@ +common: + tags: + - bluetooth + - bt_periodic_sync + - bt_ull_llcp +tests: + bluetooth.controller.ctrl_periodic_sync.test: + type: unit diff --git a/tests/bluetooth/controller/mock_ctrl/CMakeLists.txt b/tests/bluetooth/controller/mock_ctrl/CMakeLists.txt index 2e83b3d15d5f13..ccac7b9184af92 100644 --- a/tests/bluetooth/controller/mock_ctrl/CMakeLists.txt +++ b/tests/bluetooth/controller/mock_ctrl/CMakeLists.txt @@ -16,12 +16,16 @@ add_library(mocks STATIC src/assert.c src/util.c src/ticker.c + src/isoal.c src/ull.c src/ull_conn_iso.c src/ull_peripheral.c src/ull_peripheral_iso.c src/ull_central.c src/ull_scan.c + src/ull_sync.c + src/ull_adv_sync.c + src/ull_filter.c src/lll_clock.c ) diff --git a/tests/bluetooth/controller/mock_ctrl/include/hal/ticker_vendor_hal.h b/tests/bluetooth/controller/mock_ctrl/include/hal/ticker_vendor_hal.h index 503cd54aa99115..01713e3919d505 100644 --- a/tests/bluetooth/controller/mock_ctrl/include/hal/ticker_vendor_hal.h +++ b/tests/bluetooth/controller/mock_ctrl/include/hal/ticker_vendor_hal.h @@ -68,3 +68,30 @@ /* Macro defining the margin for positioning re-scheduled nodes */ #define HAL_TICKER_RESCHEDULE_MARGIN \ HAL_TICKER_US_TO_TICKS(150) + +/* Remove ticks and return positive remainder value in microseconds */ +static inline void hal_ticker_remove_jitter(uint32_t *ticks, + uint32_t *remainder) +{ + /* Is remainder less than 1 us */ + if ((*remainder & BIT(31)) || !(*remainder / HAL_TICKER_PSEC_PER_USEC)) { + *ticks -= 1U; + *remainder += HAL_TICKER_CNTR_CLK_UNIT_FSEC / HAL_TICKER_FSEC_PER_PSEC; + } + + /* pico seconds to micro seconds unit */ + *remainder /= HAL_TICKER_PSEC_PER_USEC; +} + +/* Add ticks and return positive remainder value in microseconds */ +static inline void hal_ticker_add_jitter(uint32_t *ticks, uint32_t *remainder) +{ + /* Is remainder less than 1 us */ + if ((*remainder & BIT(31)) || !(*remainder / HAL_TICKER_PSEC_PER_USEC)) { + *ticks += 1U; + *remainder += HAL_TICKER_CNTR_CLK_UNIT_FSEC / HAL_TICKER_FSEC_PER_PSEC; + } + + /* pico seconds to micro seconds unit */ + *remainder /= HAL_TICKER_PSEC_PER_USEC; +} diff --git a/tests/bluetooth/controller/mock_ctrl/include/lll/lll_adv_pdu.h b/tests/bluetooth/controller/mock_ctrl/include/lll/lll_adv_pdu.h index 5fe9872e5addbe..b94a51c2960617 100644 --- a/tests/bluetooth/controller/mock_ctrl/include/lll/lll_adv_pdu.h +++ b/tests/bluetooth/controller/mock_ctrl/include/lll/lll_adv_pdu.h @@ -113,5 +113,11 @@ static inline struct pdu_adv *lll_adv_sync_data_peek(struct lll_adv_sync *lll, v return (void *)lll->data.pdu[last]; } + +static inline struct pdu_adv *lll_adv_sync_data_curr_get(struct lll_adv_sync *lll) +{ + return (void *)lll->data.pdu[lll->data.first]; +} + #endif /* CONFIG_BT_CTLR_ADV_PERIODIC */ #endif /* CONFIG_BT_CTLR_ADV_EXT */ diff --git a/tests/bluetooth/controller/mock_ctrl/src/isoal.c b/tests/bluetooth/controller/mock_ctrl/src/isoal.c new file mode 100644 index 00000000000000..62b391babbe871 --- /dev/null +++ b/tests/bluetooth/controller/mock_ctrl/src/isoal.c @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Demant A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +uint32_t isoal_get_wrapped_time_us(uint32_t time_now_us, int32_t time_diff_us) +{ + return time_now_us; +} diff --git a/tests/bluetooth/controller/mock_ctrl/src/mayfly.c b/tests/bluetooth/controller/mock_ctrl/src/mayfly.c index cffb0b7a7205dd..ddc58419fef20d 100644 --- a/tests/bluetooth/controller/mock_ctrl/src/mayfly.c +++ b/tests/bluetooth/controller/mock_ctrl/src/mayfly.c @@ -25,7 +25,8 @@ uint32_t mayfly_is_enabled(uint8_t caller_id, uint8_t callee_id) return 0; } -uint32_t mayfly_enqueue(uint8_t caller_id, uint8_t callee_id, uint8_t chain, struct mayfly *m) +__weak uint32_t mayfly_enqueue(uint8_t caller_id, uint8_t callee_id, + uint8_t chain, struct mayfly *m) { return 0; } diff --git a/tests/bluetooth/controller/mock_ctrl/src/ticker.c b/tests/bluetooth/controller/mock_ctrl/src/ticker.c index 06518dcd6c672b..2d5381eeeb17eb 100644 --- a/tests/bluetooth/controller/mock_ctrl/src/ticker.c +++ b/tests/bluetooth/controller/mock_ctrl/src/ticker.c @@ -32,3 +32,18 @@ uint8_t ticker_stop(uint8_t instance_index, uint8_t user_id, uint8_t ticker_id, { return TICKER_STATUS_SUCCESS; } + +void ticker_job_sched(uint8_t instance_index, uint8_t user_id) +{ +} + +uint8_t ticker_next_slot_get_ext(uint8_t instance_index, uint8_t user_id, + uint8_t *ticker_id, uint32_t *ticks_current, + uint32_t *ticks_to_expire, + uint32_t *remainder, uint16_t *lazy, + ticker_op_match_func fp_match_op_func, + void *match_op_context, + ticker_op_func fp_op_func, void *op_context) +{ + return TICKER_STATUS_SUCCESS; +} diff --git a/tests/bluetooth/controller/mock_ctrl/src/ull.c b/tests/bluetooth/controller/mock_ctrl/src/ull.c index 2b9c6711813650..4a070aabe0ff1e 100644 --- a/tests/bluetooth/controller/mock_ctrl/src/ull.c +++ b/tests/bluetooth/controller/mock_ctrl/src/ull.c @@ -384,3 +384,15 @@ static inline void rx_alloc(uint8_t max) ll_rx_link_inc_quota(-1); } } + +#if defined(CONFIG_BT_CTLR_ISO) || \ + defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) || \ + defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +uint32_t ull_get_wrapped_time_us(uint32_t time_now_us, int32_t time_diff_us) +{ + return 0; +} +#endif /* CONFIG_BT_CTLR_ISO || + * CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER || + * CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER + */ diff --git a/tests/bluetooth/controller/mock_ctrl/src/ull_adv_sync.c b/tests/bluetooth/controller/mock_ctrl/src/ull_adv_sync.c new file mode 100644 index 00000000000000..135c8b70c7ad2f --- /dev/null +++ b/tests/bluetooth/controller/mock_ctrl/src/ull_adv_sync.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Demant A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include + +#include + +#include "util/mem.h" +#include "util/memq.h" +#include "pdu_df.h" +#include "lll/pdu_vendor.h" +#include "pdu.h" + +#include "lll.h" + +struct ll_adv_sync_set *ull_adv_sync_get(uint8_t handle) +{ + return NULL; +} + +uint16_t ull_adv_sync_handle_get(const struct ll_adv_sync_set *sync) +{ + return 0; +} diff --git a/tests/bluetooth/controller/mock_ctrl/src/ull_filter.c b/tests/bluetooth/controller/mock_ctrl/src/ull_filter.c new file mode 100644 index 00000000000000..b45dbd5985b8b7 --- /dev/null +++ b/tests/bluetooth/controller/mock_ctrl/src/ull_filter.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Nordic Semiconductor ASA + * Copyright (c) 2016 Vinayak Kariappa Chettimada + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include + +#include + +#include "util/mem.h" +#include "util/memq.h" +#include "pdu_df.h" +#include "lll/pdu_vendor.h" +#include "pdu.h" + +#include "lll.h" +#include "lll_filter.h" + +static uint8_t bt_addr[BDADDR_SIZE] = { 0, 0, 0, 0, 0, 0}; + +#define BT_CTLR_RL_SIZE 8 + +uint8_t ll_rl_size_get(void) +{ + return BT_CTLR_RL_SIZE; +} + +uint8_t ull_filter_rl_find(uint8_t id_addr_type, uint8_t const *const id_addr, + uint8_t *const free_idx) +{ + return FILTER_IDX_NONE; +} + +void ull_filter_rpa_update(bool timeout) +{ + +} + +const uint8_t *ull_filter_tgta_get(uint8_t rl_idx) +{ + return bt_addr; +} diff --git a/tests/bluetooth/controller/mock_ctrl/src/ull_sync.c b/tests/bluetooth/controller/mock_ctrl/src/ull_sync.c new file mode 100644 index 00000000000000..dabbeb0286e4b0 --- /dev/null +++ b/tests/bluetooth/controller/mock_ctrl/src/ull_sync.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 Nordic Semiconductor ASA + * Copyright (c) 2016 Vinayak Kariappa Chettimada + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include +#include + +#include "hal/cpu.h" +#include "hal/ccm.h" + +#include "util/util.h" +#include "util/mem.h" +#include "util/memq.h" +#include "pdu_df.h" +#include "lll/pdu_vendor.h" +#include "pdu.h" + +#include "lll.h" +#include "lll/lll_adv_types.h" +#include "lll_adv.h" +#include "ull_adv_types.h" +#include "lll_sync.h" +#include "lll_sync_iso.h" +#include "ull_sync_types.h" + +#define BT_PER_ADV_SYNC_MAX 1 + +static struct ll_sync_set ll_sync[BT_PER_ADV_SYNC_MAX]; + +struct ll_sync_set *ull_sync_set_get(uint16_t handle) +{ + if (handle >= BT_PER_ADV_SYNC_MAX) { + return NULL; + } + + return &ll_sync[handle]; +} + +struct ll_sync_set *ull_sync_is_enabled_get(uint16_t handle) +{ + struct ll_sync_set *sync; + + sync = ull_sync_set_get(handle); + if (!sync) { + return NULL; + } + + return sync; +} + +uint16_t ull_sync_handle_get(struct ll_sync_set *sync) +{ + return 0; +} diff --git a/tests/bluetooth/controller/uut/CMakeLists.txt b/tests/bluetooth/controller/uut/CMakeLists.txt index 0f976a0a5dad53..41173f1b67db99 100644 --- a/tests/bluetooth/controller/uut/CMakeLists.txt +++ b/tests/bluetooth/controller/uut/CMakeLists.txt @@ -34,6 +34,12 @@ if (CONFIG_BT_CTLR_LE_ENC) ) endif() +if (CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER OR CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + target_sources(uut PRIVATE + ${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_past.c + ) +endif() + add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl mocks) From 3f2255368f6651fcfcb71528bb566188a5f8297a Mon Sep 17 00:00:00 2001 From: Lucas Mathias Balling Date: Fri, 4 Oct 2024 13:30:20 +0200 Subject: [PATCH 3/5] Bluetooth: Controller: Implement PAST support in ULL Implement PAST support in ULL and fixed test after changed dependencies in ull_sync_internal.c Signed-off-by: Lucas Mathias Balling --- subsys/bluetooth/controller/hci/hci.c | 222 +++++- subsys/bluetooth/controller/include/ll.h | 6 + subsys/bluetooth/controller/ll_sw/isoal.c | 8 +- subsys/bluetooth/controller/ll_sw/lll.h | 1 + .../bluetooth/controller/ll_sw/lll_filter.h | 1 + .../controller/ll_sw/nordic/lll/lll_adv_pdu.h | 5 + subsys/bluetooth/controller/ll_sw/ull.c | 60 +- .../bluetooth/controller/ll_sw/ull_adv_sync.c | 59 ++ .../bluetooth/controller/ll_sw/ull_central.c | 7 +- subsys/bluetooth/controller/ll_sw/ull_conn.c | 186 ++++- .../controller/ll_sw/ull_conn_internal.h | 7 + .../controller/ll_sw/ull_conn_types.h | 13 + .../bluetooth/controller/ll_sw/ull_filter.c | 14 + .../bluetooth/controller/ll_sw/ull_internal.h | 4 + subsys/bluetooth/controller/ll_sw/ull_llcp.c | 2 +- .../controller/ll_sw/ull_peripheral.c | 7 +- .../bluetooth/controller/ll_sw/ull_scan_aux.c | 20 +- .../controller/ll_sw/ull_scan_types.h | 5 - subsys/bluetooth/controller/ll_sw/ull_sync.c | 736 ++++++++++++++---- .../controller/ll_sw/ull_sync_internal.h | 18 +- .../bluetooth/controller/ll_sw/ull_sync_iso.c | 8 + .../controller/ll_sw/ull_sync_types.h | 18 +- .../df/connectionless_cte_rx/src/common.c | 4 + 23 files changed, 1204 insertions(+), 207 deletions(-) diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c index 9217a0804d146c..8fffa01a3feafb 100644 --- a/subsys/bluetooth/controller/hci/hci.c +++ b/subsys/bluetooth/controller/hci/hci.c @@ -1036,6 +1036,19 @@ static void read_supported_commands(struct net_buf *buf, struct net_buf **evt) #endif /* CONFIG_BT_CTLR_DF */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) + /* LE Periodic Advertising Sync Transfer */ + rp->commands[40] |= BIT(6); + /* LE Periodic Advertising Set Info Transfer */ + rp->commands[40] |= BIT(7); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + /* LE Set Periodic Advertising Sync Transfer Parameters */ + rp->commands[41] |= BIT(0); + /* LE Set Default Periodic Advertising Sync Transfer Parameters */ + rp->commands[41] |= BIT(1); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + #if defined(CONFIG_BT_HCI_RAW) && defined(CONFIG_BT_TINYCRYPT_ECC) bt_hci_ecc_supported_commands(rp->commands); #endif /* CONFIG_BT_HCI_RAW && CONFIG_BT_TINYCRYPT_ECC */ @@ -4081,6 +4094,110 @@ static void le_read_pal_size(struct net_buf *buf, struct net_buf **evt) #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ #endif /* CONFIG_BT_OBSERVER */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +static void le_per_adv_sync_transfer(struct net_buf *buf, struct net_buf **evt) +{ + struct bt_hci_cp_le_per_adv_sync_transfer *cmd = (void *)buf->data; + struct bt_hci_rp_le_per_adv_sync_transfer *rp; + uint16_t conn_handle, conn_handle_le16; + uint16_t service_data; + uint16_t sync_handle; + uint8_t status; + + conn_handle_le16 = cmd->conn_handle; + + conn_handle = sys_le16_to_cpu(cmd->conn_handle); + service_data = sys_le16_to_cpu(cmd->service_data); + sync_handle = sys_le16_to_cpu(cmd->sync_handle); + + status = ll_sync_transfer(conn_handle, service_data, sync_handle); + + rp = hci_cmd_complete(evt, sizeof(*rp)); + rp->conn_handle = conn_handle_le16; + rp->status = status; +} + +static void le_per_adv_set_info_transfer(struct net_buf *buf, struct net_buf **evt) +{ + struct bt_hci_cp_le_per_adv_set_info_transfer *cmd = (void *)buf->data; + struct bt_hci_rp_le_per_adv_set_info_transfer *rp; + uint16_t conn_handle, conn_handle_le16; + uint16_t service_data; + uint8_t adv_handle; + uint8_t status; + + conn_handle_le16 = cmd->conn_handle; + + conn_handle = sys_le16_to_cpu(cmd->conn_handle); + service_data = sys_le16_to_cpu(cmd->service_data); + adv_handle = cmd->adv_handle; + + status = ll_adv_sync_set_info_transfer(conn_handle, service_data, adv_handle); + + rp = hci_cmd_complete(evt, sizeof(*rp)); + rp->conn_handle = conn_handle_le16; + rp->status = status; +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +static void le_past_param(struct net_buf *buf, struct net_buf **evt) +{ + struct bt_hci_cp_le_past_param *cmd = (void *)buf->data; + struct bt_hci_rp_le_past_param *rp; + uint16_t conn_handle_le16; + uint16_t conn_handle; + uint16_t timeout; + uint8_t cte_type; + uint8_t status; + uint16_t skip; + uint8_t mode; + + if (adv_cmds_ext_check(evt)) { + return; + } + + conn_handle_le16 = cmd->conn_handle; + + conn_handle = sys_le16_to_cpu(cmd->conn_handle); + mode = cmd->mode; + skip = sys_le16_to_cpu(cmd->skip); + timeout = sys_le16_to_cpu(cmd->timeout); + cte_type = cmd->cte_type; + + status = ll_past_param(conn_handle, mode, skip, timeout, cte_type); + + rp = hci_cmd_complete(evt, sizeof(*rp)); + rp->conn_handle = conn_handle_le16; + rp->status = status; +} + +static void le_default_past_param(struct net_buf *buf, struct net_buf **evt) +{ + struct bt_hci_cp_le_default_past_param *cmd = (void *)buf->data; + struct bt_hci_rp_le_default_past_param *rp; + uint16_t timeout; + uint8_t cte_type; + uint8_t status; + uint16_t skip; + uint8_t mode; + + if (adv_cmds_ext_check(evt)) { + return; + } + + mode = cmd->mode; + skip = sys_le16_to_cpu(cmd->skip); + timeout = sys_le16_to_cpu(cmd->timeout); + cte_type = cmd->cte_type; + + status = ll_default_past_param(mode, skip, timeout, cte_type); + + rp = hci_cmd_complete(evt, sizeof(*rp)); + rp->status = status; +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + #if defined(CONFIG_BT_CENTRAL) static void le_ext_create_connection(struct net_buf *buf, struct net_buf **evt) { @@ -4297,6 +4414,51 @@ static void le_cis_established(struct pdu_data *pdu_data, } #endif /* CONFIG_BT_CTLR_CONN_ISO */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +static void le_per_adv_sync_transfer_received(struct pdu_data *pdu_data_rx, + struct node_rx_pdu *node_rx, struct net_buf *buf) +{ + struct bt_hci_evt_le_past_received *sep; + struct node_rx_past_received *se; + struct ll_sync_set *sync; + void *node; + + if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) || + !(le_event_mask & BT_EVT_MASK_LE_PAST_RECEIVED)) { + return; + } + + sep = meta_evt(buf, BT_HCI_EVT_LE_PAST_RECEIVED, sizeof(*sep)); + + /* Check for pdu field being aligned before accessing PAST received + * event. + */ + node = pdu_data_rx; + LL_ASSERT(IS_PTR_ALIGNED(node, struct node_rx_past_received)); + + se = node; + sep->status = se->rx_sync.status; + + sync = node_rx->rx_ftr.param; + + /* Resolved address, if private, has been populated in ULL */ + sep->addr.type = sync->peer_id_addr_type; + if (sync->peer_addr_resolved) { + /* Mark it as identity address from RPA (0x02, 0x03) */ + MARK_AS_IDENTITY_ADDR(sep->addr.type); + } + (void)memcpy(sep->addr.a.val, sync->peer_id_addr, BDADDR_SIZE); + + sep->adv_sid = sync->sid; + sep->phy = find_lsb_set(se->rx_sync.phy); + sep->interval = sys_cpu_to_le16(se->rx_sync.interval); + sep->clock_accuracy = se->rx_sync.sca; + sep->conn_handle = sys_cpu_to_le16(se->conn_handle); + sep->service_data = sys_cpu_to_le16(se->service_data); + sep->sync_handle = sys_cpu_to_le16(node_rx->hdr.handle); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + static int controller_cmd_handle(uint16_t ocf, struct net_buf *cmd, struct net_buf **evt, void **node_rx) { @@ -4671,6 +4833,26 @@ static int controller_cmd_handle(uint16_t ocf, struct net_buf *cmd, #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ #endif /* CONFIG_BT_OBSERVER */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) + case BT_OCF(BT_HCI_OP_LE_PER_ADV_SYNC_TRANSFER): + le_per_adv_sync_transfer(cmd, evt); + break; + + case BT_OCF(BT_HCI_OP_LE_PER_ADV_SET_INFO_TRANSFER): + le_per_adv_set_info_transfer(cmd, evt); + break; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + case BT_OCF(BT_HCI_OP_LE_PAST_PARAM): + le_past_param(cmd, evt); + break; + + case BT_OCF(BT_HCI_OP_LE_DEFAULT_PAST_PARAM): + le_default_past_param(cmd, evt); + break; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + #if defined(CONFIG_BT_CONN) #if defined(CONFIG_BT_CENTRAL) case BT_OCF(BT_HCI_OP_LE_EXT_CREATE_CONN): @@ -6306,7 +6488,7 @@ static inline void le_dir_adv_report(struct pdu_adv *adv, struct net_buf *buf, ll_rl_id_addr_get(rl_idx, &dir_info->addr.type, &dir_info->addr.a.val[0]); /* Mark it as identity address from RPA (0x02, 0x03) */ - dir_info->addr.type += 2U; + MARK_AS_IDENTITY_ADDR(dir_info->addr.type); } else { #else if (1) { @@ -6466,7 +6648,7 @@ static void le_advertising_report(struct pdu_data *pdu_data, ll_rl_id_addr_get(rl_idx, &adv_info->addr.type, &adv_info->addr.a.val[0]); /* Mark it as identity address from RPA (0x02, 0x03) */ - adv_info->addr.type += 2U; + MARK_AS_IDENTITY_ADDR(adv_info->addr.type); } else { #else if (1) { @@ -6571,7 +6753,7 @@ static void le_ext_adv_legacy_report(struct pdu_data *pdu_data, ll_rl_id_addr_get(rl_idx, &adv_info->addr.type, &adv_info->addr.a.val[0]); /* Mark it as identity address from RPA (0x02, 0x03) */ - adv_info->addr.type += 2U; + MARK_AS_IDENTITY_ADDR(adv_info->addr.type); } else #endif /* CONFIG_BT_CTLR_PRIVACY */ { @@ -6763,7 +6945,7 @@ static void ext_adv_info_fill(uint8_t evt_type, uint8_t phy, uint8_t sec_phy, ll_rl_id_addr_get(rl_idx, &adv_info->addr.type, adv_info->addr.a.val); /* Mark it as identity address from RPA (0x02, 0x03) */ - adv_info->addr.type += 2U; + MARK_AS_IDENTITY_ADDR(adv_info->addr.type); #else /* !CONFIG_BT_CTLR_PRIVACY */ ARG_UNUSED(rl_idx); #endif /* !CONFIG_BT_CTLR_PRIVACY */ @@ -7438,7 +7620,7 @@ static void le_per_adv_sync_established(struct pdu_data *pdu_data, struct net_buf *buf) { struct bt_hci_evt_le_per_adv_sync_established *sep; - struct ll_scan_set *scan; + struct ll_sync_set *sync; struct node_rx_sync *se; void *node; @@ -7463,13 +7645,11 @@ static void le_per_adv_sync_established(struct pdu_data *pdu_data, return; } - scan = node_rx->rx_ftr.param; + sync = node_rx->rx_ftr.param; #if (CONFIG_BT_CTLR_DUP_FILTER_LEN > 0) && \ defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT) - dup_periodic_adv_reset(scan->periodic.adv_addr_type, - scan->periodic.adv_addr, - scan->periodic.sid); + dup_periodic_adv_reset(sync->peer_id_addr_type, sync->peer_id_addr, sync->sid); #endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 && * CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */ @@ -7477,10 +7657,14 @@ static void le_per_adv_sync_established(struct pdu_data *pdu_data, sep->handle = sys_cpu_to_le16(node_rx->hdr.handle); /* Resolved address, if private, has been populated in ULL */ - sep->adv_addr.type = scan->periodic.adv_addr_type; - (void)memcpy(sep->adv_addr.a.val, scan->periodic.adv_addr, BDADDR_SIZE); + sep->adv_addr.type = sync->peer_id_addr_type; + if (sync->peer_addr_resolved) { + /* Mark it as identity address from RPA (0x02, 0x03) */ + MARK_AS_IDENTITY_ADDR(sep->adv_addr.type); + } + (void)memcpy(sep->adv_addr.a.val, sync->peer_id_addr, BDADDR_SIZE); - sep->sid = scan->periodic.sid; + sep->sid = sync->sid; sep->phy = find_lsb_set(se->phy); sep->interval = sys_cpu_to_le16(se->interval); sep->clock_accuracy = se->sca; @@ -8077,7 +8261,7 @@ static void le_scan_req_received(struct pdu_data *pdu_data, ll_rl_id_addr_get(rl_idx, &sep->addr.type, &sep->addr.a.val[0]); /* Mark it as identity address from RPA (0x02, 0x03) */ - sep->addr.type += 2U; + MARK_AS_IDENTITY_ADDR(sep->addr.type); } else { #else if (1) { @@ -8117,7 +8301,7 @@ static void le_vs_scan_req_received(struct pdu_data *pdu, ll_rl_id_addr_get(rl_idx, &sep->addr.type, &sep->addr.a.val[0]); /* Mark it as identity address from RPA (0x02, 0x03) */ - sep->addr.type += 2U; + MARK_AS_IDENTITY_ADDR(sep->addr.type); } else { #else if (1) { @@ -8465,6 +8649,12 @@ static void encode_control(struct node_rx_pdu *node_rx, le_per_adv_sync_lost(pdu_data, node_rx, buf); break; +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + case NODE_RX_TYPE_SYNC_TRANSFER_RECEIVED: + le_per_adv_sync_transfer_received(pdu_data, node_rx, buf); + return; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + #if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) case NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT: #if defined(CONFIG_BT_CTLR_DF_VS_CL_IQ_REPORT_16_BITS_IQ_SAMPLES) @@ -9009,6 +9199,10 @@ uint8_t hci_get_class(struct node_rx_pdu *node_rx) case NODE_RX_TYPE_SYNC_REPORT: case NODE_RX_TYPE_SYNC_LOST: +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + case NODE_RX_TYPE_SYNC_TRANSFER_RECEIVED: +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + #if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) case NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT: #endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */ diff --git a/subsys/bluetooth/controller/include/ll.h b/subsys/bluetooth/controller/include/ll.h index 464fcc94cf9f84..482a585e962125 100644 --- a/subsys/bluetooth/controller/include/ll.h +++ b/subsys/bluetooth/controller/include/ll.h @@ -135,6 +135,12 @@ uint8_t ll_sync_create(uint8_t options, uint8_t sid, uint8_t adv_addr_type, uint8_t ll_sync_create_cancel(void **rx); uint8_t ll_sync_terminate(uint16_t handle); uint8_t ll_sync_recv_enable(uint16_t handle, uint8_t enable); +uint8_t ll_sync_transfer(uint16_t conn_handle, uint16_t service_data, uint16_t sync_handle); +uint8_t ll_adv_sync_set_info_transfer(uint16_t conn_handle, uint16_t service_data, + uint8_t adv_handle); +uint8_t ll_past_param(uint16_t conn_handle, uint8_t mode, uint16_t skip, uint16_t timeout, + uint8_t cte_type); +uint8_t ll_default_past_param(uint8_t mode, uint16_t skip, uint16_t timeout, uint8_t cte_type); uint8_t ll_big_sync_create(uint8_t big_handle, uint16_t sync_handle, uint8_t encryption, uint8_t *bcode, uint8_t mse, uint16_t sync_timeout, uint8_t num_bis, diff --git a/subsys/bluetooth/controller/ll_sw/isoal.c b/subsys/bluetooth/controller/ll_sw/isoal.c index d0c5b1a75d32cf..0d8c31f0538a4b 100644 --- a/subsys/bluetooth/controller/ll_sw/isoal.c +++ b/subsys/bluetooth/controller/ll_sw/isoal.c @@ -33,6 +33,7 @@ #include "lll_iso_tx.h" #include "isoal.h" #include "ull_iso_types.h" +#include "ull_internal.h" #include @@ -134,12 +135,7 @@ isoal_status_t isoal_reset(void) */ uint32_t isoal_get_wrapped_time_us(uint32_t time_now_us, int32_t time_diff_us) { - LL_ASSERT(time_now_us <= ISOAL_TIME_WRAPPING_POINT_US); - - uint32_t result = ((uint64_t)time_now_us + ISOAL_TIME_SPAN_FULL_US + time_diff_us) % - ((uint64_t)ISOAL_TIME_SPAN_FULL_US); - - return result; + return ull_get_wrapped_time_us(time_now_us, time_diff_us); } /** diff --git a/subsys/bluetooth/controller/ll_sw/lll.h b/subsys/bluetooth/controller/ll_sw/lll.h index 4cecde96f8ebbc..9a002e4852d36e 100644 --- a/subsys/bluetooth/controller/ll_sw/lll.h +++ b/subsys/bluetooth/controller/ll_sw/lll.h @@ -319,6 +319,7 @@ enum node_rx_type { NODE_RX_TYPE_DTM_IQ_SAMPLE_REPORT, NODE_RX_TYPE_IQ_SAMPLE_REPORT_ULL_RELEASE, NODE_RX_TYPE_IQ_SAMPLE_REPORT_LLL_RELEASE, + NODE_RX_TYPE_SYNC_TRANSFER_RECEIVED, /* Signals retention (ie non-release) of rx node */ NODE_RX_TYPE_RETAIN, diff --git a/subsys/bluetooth/controller/ll_sw/lll_filter.h b/subsys/bluetooth/controller/ll_sw/lll_filter.h index e319d6251eb7a8..efa07687f339a6 100644 --- a/subsys/bluetooth/controller/ll_sw/lll_filter.h +++ b/subsys/bluetooth/controller/ll_sw/lll_filter.h @@ -100,6 +100,7 @@ extern uint8_t ull_filter_lll_fal_match(struct lll_filter const *const filter, uint8_t *devmatch_id); extern bool ull_filter_lll_lrpa_used(uint8_t rl_idx); extern bt_addr_t *ull_filter_lll_lrpa_get(uint8_t rl_idx); +extern bt_addr_t *ull_filter_lll_id_addr_get(uint8_t rl_idx, uint8_t *id_addr_type); extern uint8_t *ull_filter_lll_irks_get(uint8_t *count); extern uint8_t ull_filter_lll_rl_idx(bool fal, uint8_t devmatch_id); extern uint8_t ull_filter_lll_rl_irk_idx(uint8_t irkmatch_id); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_pdu.h b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_pdu.h index 5d36fd0598e010..5f2af58fc6a2e6 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_pdu.h +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_adv_pdu.h @@ -195,6 +195,11 @@ lll_adv_sync_data_latest_peek(const struct lll_adv_sync *const lll) return lll_adv_pdu_latest_peek(&lll->data); } +static inline struct pdu_adv *lll_adv_sync_data_curr_get(struct lll_adv_sync *lll) +{ + return (void *)lll->data.pdu[lll->data.first]; +} + #if defined(CONFIG_BT_CTLR_ADV_EXT_PDU_EXTRA_DATA_MEMORY) static inline void *lll_adv_sync_extra_data_peek(struct lll_adv_sync *lll) { diff --git a/subsys/bluetooth/controller/ll_sw/ull.c b/subsys/bluetooth/controller/ll_sw/ull.c index 5cf20090a00a7b..8b31ce846ab67e 100644 --- a/subsys/bluetooth/controller/ll_sw/ull.c +++ b/subsys/bluetooth/controller/ll_sw/ull.c @@ -506,6 +506,16 @@ static struct { static MEMQ_DECLARE(ull_rx); static MEMQ_DECLARE(ll_rx); +#if defined(CONFIG_BT_CTLR_ISO) || \ + defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) || \ + defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +#define ULL_TIME_WRAPPING_POINT_US (HAL_TICKER_TICKS_TO_US_64BIT(HAL_TICKER_CNTR_MASK)) +#define ULL_TIME_SPAN_FULL_US (ULL_TIME_WRAPPING_POINT_US + 1) +#endif /* CONFIG_BT_CTLR_ISO || + * CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER || + * CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER + */ + #if defined(CONFIG_BT_CONN) static MFIFO_DEFINE(ll_pdu_rx_free, sizeof(void *), LL_PDU_RX_CNT); @@ -1267,6 +1277,10 @@ void ll_rx_dequeue(void) /* fall through */ case NODE_RX_TYPE_SYNC: case NODE_RX_TYPE_SYNC_LOST: +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + /* fall through */ + case NODE_RX_TYPE_SYNC_TRANSFER_RECEIVED: +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ #if defined(CONFIG_BT_CTLR_SYNC_ISO) /* fall through */ case NODE_RX_TYPE_SYNC_ISO: @@ -1544,6 +1558,9 @@ void ll_rx_mem_release(void **node_rx) break; #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + case NODE_RX_TYPE_SYNC_TRANSFER_RECEIVED: +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ case NODE_RX_TYPE_SYNC: { struct node_rx_sync *se = @@ -1558,21 +1575,15 @@ void ll_rx_mem_release(void **node_rx) (status == BT_HCI_ERR_UNSUPP_REMOTE_FEATURE) || (status == BT_HCI_ERR_CONN_FAIL_TO_ESTAB)) { struct ll_sync_set *sync; - struct ll_scan_set *scan; - /* pick the scan context before node_rx + /* pick the sync context before node_rx * release. */ - scan = (void *)rx_free->rx_ftr.param; + sync = (void *)rx_free->rx_ftr.param; ll_rx_release(rx_free); - /* pick the sync context before scan context - * is cleanup of sync context association. - */ - sync = scan->periodic.sync; - - ull_sync_setup_reset(scan); + ull_sync_setup_reset(sync); if (status != BT_HCI_ERR_SUCCESS) { memq_link_t *link_sync_lost; @@ -2812,6 +2823,14 @@ static inline void rx_demux_rx(memq_link_t *link, struct node_rx_hdr *rx) ull_sync_established_report(link, (struct node_rx_pdu *)rx); } break; +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + case NODE_RX_TYPE_SYNC_TRANSFER_RECEIVED: + { + (void)memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL); + ll_rx_put_sched(link, rx); + } + break; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */ #endif /* CONFIG_BT_CTLR_ADV_EXT */ #endif /* CONFIG_BT_OBSERVER */ @@ -3114,3 +3133,26 @@ void *ull_rxfifo_release(uint8_t s, uint8_t n, uint8_t f, uint8_t *l, uint8_t *m return rx; } + +#if defined(CONFIG_BT_CTLR_ISO) || \ + defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) || \ + defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +/** + * @brief Wraps given time within the range of 0 to ULL_TIME_WRAPPING_POINT_US + * @param time_now Current time value + * @param time_diff Time difference (signed) + * @return Wrapped time after difference + */ +uint32_t ull_get_wrapped_time_us(uint32_t time_now_us, int32_t time_diff_us) +{ + LL_ASSERT(time_now_us <= ULL_TIME_WRAPPING_POINT_US); + + uint32_t result = ((uint64_t)time_now_us + ULL_TIME_SPAN_FULL_US + time_diff_us) % + ((uint64_t)ULL_TIME_SPAN_FULL_US); + + return result; +} +#endif /* CONFIG_BT_CTLR_ISO || + * CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER || + * CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER + */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_adv_sync.c b/subsys/bluetooth/controller/ll_sw/ull_adv_sync.c index c19ec42b645276..fb111a02f8b3af 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_adv_sync.c +++ b/subsys/bluetooth/controller/ll_sw/ull_adv_sync.c @@ -43,6 +43,14 @@ #include "ull_sched_internal.h" #include "ull_adv_internal.h" +#include "ull_conn_internal.h" + +#include "isoal.h" +#include "ull_iso_types.h" +#include "lll_conn_iso.h" +#include "ull_conn_iso_types.h" +#include "ull_llcp.h" + #include "ll.h" #include "hal/debug.h" @@ -621,6 +629,51 @@ uint8_t ll_adv_sync_enable(uint8_t handle, uint8_t enable) return 0; } +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +/* @brief Link Layer interface function corresponding to HCI LE Periodic + * Advertising Set Info Transfer command. + * + * @param[in] conn_handle Connection_Handle identifying the connected device + * Range: 0x0000 to 0x0EFF. + * @param[in] service_data Service_Data value provided by the Host for use by the + * Host of the peer device. + * @param[in] adv_handle Advertising_Handle identifying the advertising + * set. Range: 0x00 to 0xEF. + * + * @return HCI error codes as documented in Bluetooth Core Specification v5.4. + */ +uint8_t ll_adv_sync_set_info_transfer(uint16_t conn_handle, uint16_t service_data, + uint8_t adv_handle) +{ + struct ll_adv_sync_set *sync; + struct ll_adv_set *adv; + struct ll_conn *conn; + + conn = ll_connected_get(conn_handle); + if (!conn) { + return BT_HCI_ERR_UNKNOWN_CONN_ID; + } + + /* Verify that adv_handle is valid and periodic advertising is enabled */ + adv = ull_adv_is_created_get(adv_handle); + if (!adv) { + return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER; + } + + if (!adv->lll.sync) { + return BT_HCI_ERR_CMD_DISALLOWED; + } + + sync = HDR_LLL2ULL(adv->lll.sync); + if (!sync->is_enabled) { + return BT_HCI_ERR_CMD_DISALLOWED; + } + + /* Call llcp to start LLCP_PERIODIC_SYNC_IND */ + return ull_cp_periodic_sync(conn, NULL, sync, service_data); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + int ull_adv_sync_init(void) { int err; @@ -945,8 +998,10 @@ void ull_adv_sync_pdu_init(struct pdu_adv *pdu, uint8_t ext_hdr_flags, { struct pdu_adv_com_ext_adv *com_hdr; struct pdu_adv_ext_hdr *ext_hdr; +#if defined(CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK) struct pdu_adv_aux_ptr *aux_ptr; uint32_t cte_len_us; +#endif uint8_t *dptr; uint8_t len; @@ -971,6 +1026,7 @@ void ull_adv_sync_pdu_init(struct pdu_adv *pdu, uint8_t ext_hdr_flags, #endif /* CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT */ ULL_ADV_PDU_HDR_FIELD_SYNC_INFO))); +#if defined(CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK) if (IS_ENABLED(CONFIG_BT_CTLR_DF_ADV_CTE_TX) && (ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_CTE_INFO)) { (void)memcpy(dptr, cte_info, sizeof(*cte_info)); @@ -979,15 +1035,18 @@ void ull_adv_sync_pdu_init(struct pdu_adv *pdu, uint8_t ext_hdr_flags, } else { cte_len_us = 0U; } +#endif /* CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK */ if (IS_ENABLED(CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT) && (ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_ADI)) { dptr += sizeof(struct pdu_adv_adi); } +#if defined(CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK) if (IS_ENABLED(CONFIG_BT_CTLR_ADV_SYNC_PDU_LINK) && (ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_AUX_PTR)) { aux_ptr = (void *)dptr; dptr += sizeof(struct pdu_adv_aux_ptr); } +#endif /* CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK */ if (ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_TX_POWER) { dptr += sizeof(uint8_t); } diff --git a/subsys/bluetooth/controller/ll_sw/ull_central.c b/subsys/bluetooth/controller/ll_sw/ull_central.c index 397c0d0b5f9da5..7f60f06af92dd0 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_central.c +++ b/subsys/bluetooth/controller/ll_sw/ull_central.c @@ -722,7 +722,7 @@ void ull_central_setup(struct node_rx_pdu *rx, struct node_rx_ftr *ftr, ll_rl_id_addr_get(rl_idx, &cc->peer_addr_type, &cc->peer_addr[0]); /* Mark it as identity address from RPA (0x02, 0x03) */ - cc->peer_addr_type += 2; + MARK_AS_IDENTITY_ADDR(cc->peer_addr_type); /* Store peer RPA */ memcpy(&cc->peer_rpa[0], &peer_addr[0], BDADDR_SIZE); @@ -749,6 +749,11 @@ void ull_central_setup(struct node_rx_pdu *rx, struct node_rx_ftr *ftr, /* Set LLCP as connection-wise connected */ ull_cp_state_set(conn, ULL_CP_CONNECTED); +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + /* Set default PAST parameters */ + conn->past = ull_conn_default_past_param_get(); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + #if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL) lll->tx_pwr_lvl = RADIO_TXP_DEFAULT; #endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn.c b/subsys/bluetooth/controller/ll_sw/ull_conn.c index 1af9bacea20c73..7335473556d635 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn.c +++ b/subsys/bluetooth/controller/ll_sw/ull_conn.c @@ -57,7 +57,16 @@ #include "ull_iso_internal.h" #include "ull_conn_iso_internal.h" #include "ull_peripheral_iso_internal.h" - +#include "lll/lll_adv_types.h" +#include "lll_adv.h" +#include "ull_adv_types.h" +#include "ull_adv_internal.h" +#include "lll_sync.h" +#include "lll_sync_iso.h" +#include "ull_sync_types.h" +#include "lll_scan.h" +#include "ull_scan_types.h" +#include "ull_sync_internal.h" #include "ll.h" #include "ll_feat.h" @@ -147,6 +156,10 @@ static uint8_t default_phy_tx; static uint8_t default_phy_rx; #endif /* CONFIG_BT_CTLR_PHY */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +static struct past_params default_past_params; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + static struct ll_conn conn_pool[CONFIG_BT_MAX_CONN]; static void *conn_free; @@ -795,6 +808,22 @@ uint8_t ull_conn_default_phy_rx_get(void) } #endif /* CONFIG_BT_CTLR_PHY */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +void ull_conn_default_past_param_set(uint8_t mode, uint16_t skip, uint16_t timeout, + uint8_t cte_type) +{ + default_past_params.mode = mode; + default_past_params.skip = skip; + default_past_params.timeout = timeout; + default_past_params.cte_type = cte_type; +} + +struct past_params ull_conn_default_past_param_get(void) +{ + return default_past_params; +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + #if defined(CONFIG_BT_CTLR_CHECK_SAME_PEER_CONN) bool ull_conn_peer_connected(uint8_t const own_id_addr_type, uint8_t const *const own_id_addr, @@ -958,6 +987,10 @@ void ull_conn_done(struct node_rx_event_done *done) ull_cp_tx_ntf(conn); +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) + ull_lp_past_conn_evt_done(conn, done); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + #if defined(CONFIG_BT_CTLR_LE_ENC) /* Check authenticated payload expiry or MIC failure */ switch (done->extra.mic_state) { @@ -1625,6 +1658,10 @@ static int init_reset(void) #endif /* CONFIG_BT_CTLR_PHY_CODED */ #endif /* CONFIG_BT_CTLR_PHY */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + memset(&default_past_params, 0, sizeof(struct past_params)); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + return 0; } @@ -2563,6 +2600,153 @@ void ull_conn_default_tx_time_set(uint16_t tx_time) } #endif /* CONFIG_BT_CTLR_DATA_LENGTH */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +static bool ticker_op_id_match_func(uint8_t ticker_id, uint32_t ticks_slot, + uint32_t ticks_to_expire, void *op_context) +{ + ARG_UNUSED(ticks_slot); + ARG_UNUSED(ticks_to_expire); + + uint8_t match_id = *(uint8_t *)op_context; + + return ticker_id == match_id; +} + +static void ticker_get_offset_op_cb(uint32_t status, void *param) +{ + *((uint32_t volatile *)param) = status; +} + +static uint32_t get_ticker_offset(uint8_t ticker_id, uint16_t *lazy) +{ + uint32_t volatile ret_cb; + uint32_t ticks_to_expire; + uint32_t ticks_current; + uint32_t sync_remainder_us; + uint32_t remainder; + uint32_t start_us; + uint32_t ret; + uint8_t id; + + id = TICKER_NULL; + ticks_to_expire = 0U; + ticks_current = 0U; + + ret_cb = TICKER_STATUS_BUSY; + + ret = ticker_next_slot_get_ext(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_LOW, + &id, &ticks_current, &ticks_to_expire, &remainder, + lazy, ticker_op_id_match_func, &ticker_id, + ticker_get_offset_op_cb, (void *)&ret_cb); + + if (ret == TICKER_STATUS_BUSY) { + while (ret_cb == TICKER_STATUS_BUSY) { + ticker_job_sched(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_LOW); + } + } + + LL_ASSERT(ret_cb == TICKER_STATUS_SUCCESS); + + /* Reduced a tick for negative remainder and return positive remainder + * value. + */ + hal_ticker_remove_jitter(&ticks_to_expire, &remainder); + sync_remainder_us = remainder; + + /* Add a tick for negative remainder and return positive remainder + * value. + */ + hal_ticker_add_jitter(&ticks_to_expire, &remainder); + start_us = remainder; + + return ull_get_wrapped_time_us(HAL_TICKER_TICKS_TO_US(ticks_to_expire), + (sync_remainder_us - start_us)); +} + +static void mfy_past_sender_offset_get(void *param) +{ + uint16_t last_pa_event_counter; + uint32_t ticker_offset_us; + uint16_t pa_event_counter; + uint8_t adv_sync_handle; + uint16_t sync_handle; + struct ll_conn *conn; + uint16_t lazy; + + conn = param; + + /* Get handle to look for */ + ull_lp_past_offset_get_calc_params(conn, &adv_sync_handle, &sync_handle); + + if (adv_sync_handle == BT_HCI_ADV_HANDLE_INVALID && + sync_handle == BT_HCI_SYNC_HANDLE_INVALID) { + /* Procedure must have been aborted, do nothing */ + return; + } + + if (adv_sync_handle != BT_HCI_ADV_HANDLE_INVALID) { + const struct ll_adv_sync_set *adv_sync = ull_adv_sync_get(adv_sync_handle); + + LL_ASSERT(adv_sync); + + ticker_offset_us = get_ticker_offset(TICKER_ID_ADV_SYNC_BASE + adv_sync_handle, + &lazy); + + pa_event_counter = adv_sync->lll.event_counter; + last_pa_event_counter = pa_event_counter - 1; + } else { + const struct ll_sync_set *sync = ull_sync_is_enabled_get(sync_handle); + uint32_t interval_us = sync->interval * PERIODIC_INT_UNIT_US; + uint32_t window_widening_event_us; + + LL_ASSERT(sync); + + ticker_offset_us = get_ticker_offset(TICKER_ID_SCAN_SYNC_BASE + sync_handle, + &lazy); + + if (lazy && ticker_offset_us > interval_us) { + + /* Figure out how many events we have actually skipped */ + lazy = lazy - (ticker_offset_us / interval_us); + + /* Correct offset to point to next event */ + ticker_offset_us = ticker_offset_us % interval_us; + } + + /* Calculate window widening for next event */ + window_widening_event_us = sync->lll.window_widening_event_us + + sync->lll.window_widening_periodic_us * (lazy + 1U); + + /* Correct for window widening */ + ticker_offset_us += window_widening_event_us; + + pa_event_counter = sync->lll.event_counter + lazy; + + last_pa_event_counter = pa_event_counter - 1 - lazy; + + /* Handle unsuccessful events */ + if (sync->timeout_expire) { + last_pa_event_counter -= sync->timeout_reload - sync->timeout_expire; + } + } + + ull_lp_past_offset_calc_reply(conn, ticker_offset_us, pa_event_counter, + last_pa_event_counter); +} + +void ull_conn_past_sender_offset_request(struct ll_conn *conn) +{ + static memq_link_t link; + static struct mayfly mfy = {0, 0, &link, NULL, mfy_past_sender_offset_get}; + uint32_t ret; + + mfy.param = conn; + ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 1, + &mfy); + LL_ASSERT(!ret); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + uint8_t ull_conn_lll_phy_active(struct ll_conn *conn, uint8_t phys) { #if defined(CONFIG_BT_CTLR_PHY) diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_internal.h b/subsys/bluetooth/controller/ll_sw/ull_conn_internal.h index 69b18bde3fe6f5..dad6d5e29e0e19 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_internal.h @@ -17,6 +17,9 @@ uint16_t ull_conn_default_tx_octets_get(void); uint16_t ull_conn_default_tx_time_get(void); uint8_t ull_conn_default_phy_tx_get(void); uint8_t ull_conn_default_phy_rx_get(void); +void ull_conn_default_past_param_set(uint8_t mode, uint16_t skip, uint16_t timeout, + uint8_t cte_type); +struct past_params ull_conn_default_past_param_get(void); bool ull_conn_peer_connected(uint8_t const own_id_addr_type, uint8_t const *const own_id_addr, uint8_t const peer_id_addr_type, @@ -77,6 +80,10 @@ static inline void cpr_active_reset(void) } #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +void ull_conn_past_sender_offset_request(struct ll_conn *conn); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + uint16_t ull_conn_event_counter(struct ll_conn *conn); void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h index ac466b73dc622b..4b77c22b49991d 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h @@ -154,10 +154,23 @@ struct llcp_struct { }; /* struct llcp_struct */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +struct past_params { + uint8_t mode; + uint8_t cte_type; + uint16_t skip; + uint16_t timeout; +}; /* struct past_params */ +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + struct ll_conn { struct ull_hdr ull; struct lll_conn lll; +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + struct past_params past; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + struct ull_tx_q tx_q; struct llcp_struct llcp; diff --git a/subsys/bluetooth/controller/ll_sw/ull_filter.c b/subsys/bluetooth/controller/ll_sw/ull_filter.c index 5d6e68238aed14..f80dda228e50d4 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_filter.c +++ b/subsys/bluetooth/controller/ll_sw/ull_filter.c @@ -859,6 +859,20 @@ bt_addr_t *ull_filter_lll_lrpa_get(uint8_t rl_idx) return rl[rl_idx].local_rpa; } +bt_addr_t *ull_filter_lll_id_addr_get(uint8_t rl_idx, uint8_t *id_addr_type) +{ + struct lll_resolve_list *rl_entry; + + if (rl_idx >= ARRAY_SIZE(rl)) { + return NULL; + } + + rl_entry = &rl[rl_idx]; + *id_addr_type = rl_entry->id_addr_type; + + return &rl_entry->id_addr; +} + uint8_t *ull_filter_lll_irks_get(uint8_t *count) { *count = peer_irk_count; diff --git a/subsys/bluetooth/controller/ll_sw/ull_internal.h b/subsys/bluetooth/controller/ll_sw/ull_internal.h index e2cdaa41814030..8218d1da61bd08 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_internal.h @@ -45,6 +45,9 @@ extern bool ull_handle_cpr_anchor_point_move(struct ll_conn *conn, uint16_t *off /* Macro to convert time in us to periodic advertising interval units */ #define RADIO_SYNC_EVENTS(x, y) ((uint16_t)DIV_ROUND_UP(x, y)) +/* Macro to mark address type as identity address from RPA (0x02, 0x03) */ +#define MARK_AS_IDENTITY_ADDR(addr_type) ((addr_type) += 2U) + static inline uint8_t ull_ref_get(struct ull_hdr *hdr) { return hdr->ref; @@ -170,3 +173,4 @@ void ull_rxfifo_alloc(uint8_t s, uint8_t n, uint8_t f, uint8_t *l, uint8_t *m, void *mem_free, void *link_free, uint8_t max); void *ull_rxfifo_release(uint8_t s, uint8_t n, uint8_t f, uint8_t *l, uint8_t *m, memq_link_t *link, struct node_rx_hdr *rx); +uint32_t ull_get_wrapped_time_us(uint32_t time_now_us, int32_t time_diff_us); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.c b/subsys/bluetooth/controller/ll_sw/ull_llcp.c index 1540f043cb301e..14754090934fcf 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.c @@ -1520,7 +1520,7 @@ void ull_lp_past_conn_evt_done(struct ll_conn *conn, struct node_rx_event_done * if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL && done->extra.trx_cnt) { uint32_t start_to_actual_us; - start_to_actual_us = isoal_get_wrapped_time_us( + start_to_actual_us = ull_get_wrapped_time_us( done->extra.drift.start_to_address_actual_us, (-done->extra.drift.preamble_to_addr_us)); diff --git a/subsys/bluetooth/controller/ll_sw/ull_peripheral.c b/subsys/bluetooth/controller/ll_sw/ull_peripheral.c index cb2eda865927b7..0aa64d1afd36e2 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_peripheral.c +++ b/subsys/bluetooth/controller/ll_sw/ull_peripheral.c @@ -109,7 +109,7 @@ void ull_periph_setup(struct node_rx_pdu *rx, struct node_rx_ftr *ftr, /* Get identity address */ ll_rl_id_addr_get(rl_idx, &peer_addr_type, peer_id_addr); /* Mark it as identity address from RPA (0x02, 0x03) */ - peer_addr_type += 2; + MARK_AS_IDENTITY_ADDR(peer_addr_type); } else { #else /* CONFIG_BT_CTLR_PRIVACY */ if (1) { @@ -144,6 +144,11 @@ void ull_periph_setup(struct node_rx_pdu *rx, struct node_rx_ftr *ftr, sizeof(conn->own_id_addr)); #endif /* CONFIG_BT_CTLR_CHECK_SAME_PEER_CONN */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + /* Set default PAST parameters */ + conn->past = ull_conn_default_past_param_get(); +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + memcpy(&lll->crc_init[0], &pdu_adv->connect_ind.crc_init[0], 3); memcpy(&lll->access_addr[0], &pdu_adv->connect_ind.access_addr[0], 4); memcpy(&lll->data_chan_map[0], &pdu_adv->connect_ind.chan_map[0], diff --git a/subsys/bluetooth/controller/ll_sw/ull_scan_aux.c b/subsys/bluetooth/controller/ll_sw/ull_scan_aux.c index 93a826506cddb0..ee84bba6e53b5f 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_scan_aux.c +++ b/subsys/bluetooth/controller/ll_sw/ull_scan_aux.c @@ -5,6 +5,7 @@ */ #include +#include #include #include "util/mem.h" @@ -28,14 +29,27 @@ #include "lll_scan_aux.h" #include "lll/lll_df_types.h" #include "lll_conn.h" +#include "lll_conn_iso.h" #include "lll_sync.h" #include "lll_sync_iso.h" +#include "lll/lll_adv_types.h" +#include "lll_adv.h" +#include "lll/lll_adv_pdu.h" +#include "ll_sw/ull_tx_queue.h" + +#include "isoal.h" #include "ull_scan_types.h" +#include "ull_conn_types.h" +#include "ull_iso_types.h" +#include "ull_conn_iso_types.h" #include "ull_sync_types.h" +#include "ull_adv_types.h" +#include "ull_adv_internal.h" #include "ull_internal.h" #include "ull_scan_internal.h" +#include "ull_conn_internal.h" #include "ull_sync_internal.h" #include "ull_sync_iso_internal.h" #include "ull_df_internal.h" @@ -377,10 +391,10 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_pdu *rx) if (sync && (scan->periodic.state != LL_SYNC_STATE_CREATED)) { /* Check address and update internal state */ #if defined(CONFIG_BT_CTLR_PRIVACY) - ull_sync_setup_addr_check(scan, pdu->tx_addr, ptr, + ull_sync_setup_addr_check(sync, scan, pdu->tx_addr, ptr, ftr->rl_idx); #else /* !CONFIG_BT_CTLR_PRIVACY */ - ull_sync_setup_addr_check(scan, pdu->tx_addr, ptr, 0U); + ull_sync_setup_addr_check(sync, scan, pdu->tx_addr, ptr, 0U); #endif /* !CONFIG_BT_CTLR_PRIVACY */ } @@ -420,7 +434,7 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_pdu *rx) * Periodic Advertiser List or with the explicitly supplied. */ if (IS_ENABLED(CONFIG_BT_CTLR_SYNC_PERIODIC) && aux && sync && adi && - ull_sync_setup_sid_match(scan, PDU_ADV_ADI_SID_GET(adi))) { + ull_sync_setup_sid_match(sync, scan, PDU_ADV_ADI_SID_GET(adi))) { ull_sync_setup(scan, aux, rx, si); } } diff --git a/subsys/bluetooth/controller/ll_sw/ull_scan_types.h b/subsys/bluetooth/controller/ll_sw/ull_scan_types.h index 41c2bbd4628dce..ab91448f34cf33 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_scan_types.h +++ b/subsys/bluetooth/controller/ll_sw/ull_scan_types.h @@ -22,15 +22,10 @@ struct ll_scan_set { #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) struct { - uint8_t sid; - - uint8_t adv_addr_type:2; uint8_t filter_policy:1; uint8_t cancelled:1; uint8_t state:2; - uint8_t adv_addr[BDADDR_SIZE]; - /* Non-Null when creating sync, reset in ISR context on * synchronisation state and checked in Thread context when * cancelling sync create, hence the volatile keyword. diff --git a/subsys/bluetooth/controller/ll_sw/ull_sync.c b/subsys/bluetooth/controller/ll_sw/ull_sync.c index 900e35cecd657e..e151f9b8f47036 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_sync.c +++ b/subsys/bluetooth/controller/ll_sw/ull_sync.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -27,25 +28,41 @@ #include "pdu.h" #include "lll.h" +#include "lll/lll_adv_types.h" +#include "lll_adv.h" +#include "lll/lll_adv_pdu.h" #include "lll_clock.h" #include "lll/lll_vendor.h" #include "lll_chan.h" #include "lll_scan.h" #include "lll/lll_df_types.h" #include "lll_conn.h" +#include "lll_conn_iso.h" #include "lll_sync.h" #include "lll_sync_iso.h" +#include "isoal.h" + +#include "ull_tx_queue.h" + #include "ull_filter.h" +#include "ull_iso_types.h" #include "ull_scan_types.h" #include "ull_sync_types.h" +#include "ull_conn_types.h" +#include "ull_adv_types.h" +#include "ull_conn_iso_types.h" #include "ull_internal.h" +#include "ull_adv_internal.h" #include "ull_scan_internal.h" #include "ull_sync_internal.h" +#include "ull_conn_internal.h" +#include "ull_conn_iso_internal.h" #include "ull_df_types.h" #include "ull_df_internal.h" +#include "ull_llcp.h" #include "ll.h" #include @@ -59,6 +76,8 @@ */ MEM_FREE_MEMBER_ACCESS_BUILD_ASSERT(struct ll_sync_set, timeout_reload); +static struct ll_sync_set *ull_sync_create(uint8_t sid, uint16_t timeout, uint16_t skip, + uint8_t cte_type, uint8_t rx_enable, uint8_t nodups); static int init_reset(void); static inline struct ll_sync_set *sync_acquire(void); static void sync_ticker_cleanup(struct ll_sync_set *sync, ticker_op_func stop_op_cb); @@ -98,12 +117,10 @@ uint8_t ll_sync_create(uint8_t options, uint8_t sid, uint8_t adv_addr_type, uint16_t sync_timeout, uint8_t sync_cte_type) { struct ll_scan_set *scan_coded; - memq_link_t *link_sync_estab; - memq_link_t *link_sync_lost; - struct node_rx_pdu *node_rx; - struct lll_sync *lll_sync; struct ll_scan_set *scan; struct ll_sync_set *sync; + uint8_t rx_enable; + uint8_t nodups; scan = ull_scan_set_get(SCAN_HANDLE_1M); if (!scan || scan->periodic.sync) { @@ -125,32 +142,11 @@ uint8_t ll_sync_create(uint8_t options, uint8_t sid, uint8_t adv_addr_type, } #endif /* CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC */ - link_sync_estab = ll_rx_link_alloc(); - if (!link_sync_estab) { - return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; - } - - link_sync_lost = ll_rx_link_alloc(); - if (!link_sync_lost) { - ll_rx_link_release(link_sync_estab); + rx_enable = !(options & BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_REPORTS_DISABLED); + nodups = (options & BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_FILTER_DUPLICATE) ? 1U : 0U; - return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; - } - - node_rx = ll_rx_alloc(); - if (!node_rx) { - ll_rx_link_release(link_sync_lost); - ll_rx_link_release(link_sync_estab); - - return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; - } - - sync = sync_acquire(); + sync = ull_sync_create(sid, sync_timeout, skip, sync_cte_type, rx_enable, nodups); if (!sync) { - ll_rx_release(node_rx); - ll_rx_link_release(link_sync_lost); - ll_rx_link_release(link_sync_estab); - return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; } @@ -166,55 +162,10 @@ uint8_t ll_sync_create(uint8_t options, uint8_t sid, uint8_t adv_addr_type, } if (!scan->periodic.filter_policy) { - scan->periodic.sid = sid; - scan->periodic.adv_addr_type = adv_addr_type; - (void)memcpy(scan->periodic.adv_addr, adv_addr, BDADDR_SIZE); - - if (IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED)) { - scan_coded->periodic.sid = scan->periodic.sid; - scan_coded->periodic.adv_addr_type = - scan->periodic.adv_addr_type; - (void)memcpy(scan_coded->periodic.adv_addr, - scan->periodic.adv_addr, BDADDR_SIZE); - } + sync->peer_id_addr_type = adv_addr_type; + (void)memcpy(sync->peer_id_addr, adv_addr, BDADDR_SIZE); } - /* Initialize sync context */ - node_rx->hdr.link = link_sync_estab; - sync->node_rx_lost.rx.hdr.link = link_sync_lost; - - /* Make sure that the node_rx_sync_establ hasn't got anything assigned. It is used to - * mark when sync establishment is in progress. - */ - LL_ASSERT(!sync->node_rx_sync_estab); - sync->node_rx_sync_estab = node_rx; - - /* Reporting initially enabled/disabled */ - sync->rx_enable = - !(options & BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_REPORTS_DISABLED); - -#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT) - sync->nodups = (options & - BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_FILTER_DUPLICATE) ? - 1U : 0U; -#endif - sync->skip = skip; - sync->is_stop = 0U; - -#if defined(CONFIG_BT_CTLR_SYNC_ISO) - sync->enc = 0U; -#endif /* CONFIG_BT_CTLR_SYNC_ISO */ - - /* NOTE: Use timeout not zero to represent sync context used for sync - * create. - */ - sync->timeout = sync_timeout; - - /* NOTE: Use timeout_reload not zero to represent sync established. */ - sync->timeout_reload = 0U; - sync->timeout_expire = 0U; - -#if defined(CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC) /* Remember the peer address when periodic advertiser list is not * used. * NOTE: Peer address will be filled/overwritten with correct identity @@ -226,42 +177,11 @@ uint8_t ll_sync_create(uint8_t options, uint8_t sid, uint8_t adv_addr_type, sizeof(sync->peer_id_addr)); } - /* Remember the SID */ - sync->sid = sid; -#endif /* CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC */ - -#if defined(CONFIG_BT_CTLR_SYNC_ISO) - /* Reset Broadcast Isochronous Group Sync Establishment */ - sync->iso.sync_iso = NULL; -#endif /* CONFIG_BT_CTLR_SYNC_ISO */ - - /* Initialize sync LLL context */ - lll_sync = &sync->lll; - lll_sync->lll_aux = NULL; - lll_sync->is_rx_enabled = sync->rx_enable; - lll_sync->skip_prepare = 0U; - lll_sync->skip_event = 0U; - lll_sync->window_widening_prepare_us = 0U; - lll_sync->window_widening_event_us = 0U; #if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING) - lll_sync->cte_type = sync_cte_type; - lll_sync->filter_policy = scan->periodic.filter_policy; + /* Set filter policy in lll_sync */ + sync->lll.filter_policy = scan->periodic.filter_policy; #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING */ -#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) - ull_df_sync_cfg_init(&lll_sync->df_cfg); - LL_ASSERT(!lll_sync->node_cte_incomplete); -#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */ - - /* Initialise ULL and LLL headers */ - ull_hdr_init(&sync->ull); - lll_hdr_init(lll_sync, sync); - -#if defined(CONFIG_BT_CTLR_SCAN_AUX_SYNC_RESERVE_MIN) - /* Initialise LLL abort count */ - lll_sync->abort_count = 0U; -#endif /* CONFIG_BT_CTLR_SCAN_AUX_SYNC_RESERVE_MIN */ - /* Enable scanner to create sync */ scan->periodic.sync = sync; @@ -279,6 +199,243 @@ uint8_t ll_sync_create(uint8_t options, uint8_t sid, uint8_t adv_addr_type, return 0; } +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +void ull_sync_setup_from_sync_transfer(struct ll_conn *conn, uint16_t service_data, + struct ll_sync_set *sync, struct pdu_adv_sync_info *si, + int16_t conn_evt_offset, uint16_t last_pa_event_counter, + uint16_t sync_conn_event_count, uint8_t sender_sca) +{ + struct node_rx_past_received *se_past; + uint32_t ticks_slot_overhead; + uint32_t ticks_slot_offset; + uint32_t conn_interval_us; + uint32_t sync_offset_us; + uint32_t ready_delay_us; + struct node_rx_pdu *rx; + uint8_t *data_chan_map; + struct lll_sync *lll; + uint32_t interval_us; + uint32_t slot_us; + uint32_t ticks_anchor; + uint8_t chm_last; + uint32_t ret; + uint16_t interval; + uint16_t sync_handle; + uint8_t sca; + + lll = &sync->lll; + + /* Copy channel map from sca_chm field in sync_info structure, and + * clear the SCA bits. + */ + chm_last = lll->chm_first; + lll->chm_last = chm_last; + data_chan_map = lll->chm[chm_last].data_chan_map; + (void)memcpy(data_chan_map, si->sca_chm, + sizeof(lll->chm[chm_last].data_chan_map)); + data_chan_map[PDU_SYNC_INFO_SCA_CHM_SCA_BYTE_OFFSET] &= + ~PDU_SYNC_INFO_SCA_CHM_SCA_BIT_MASK; + lll->chm[chm_last].data_chan_count = + util_ones_count_get(data_chan_map, + sizeof(lll->chm[chm_last].data_chan_map)); + if (lll->chm[chm_last].data_chan_count < CHM_USED_COUNT_MIN) { + /* Ignore sync setup, invalid available channel count */ + return; + } + + memcpy(lll->access_addr, si->aa, sizeof(lll->access_addr)); + lll->data_chan_id = lll_chan_id(lll->access_addr); + memcpy(lll->crc_init, si->crc_init, sizeof(lll->crc_init)); + lll->event_counter = sys_le16_to_cpu(si->evt_cntr); + + interval = sys_le16_to_cpu(si->interval); + interval_us = interval * PERIODIC_INT_UNIT_US; + + /* Convert fromm 10ms units to interval units */ + if (sync->timeout != 0 && interval_us != 0) { + sync->timeout_reload = RADIO_SYNC_EVENTS((sync->timeout * 10U * + USEC_PER_MSEC), interval_us); + } + + /* Adjust Skip value so that there is minimum of 6 events that can be + * listened to before Sync_Timeout occurs. + * The adjustment of the skip value is controller implementation + * specific and not specified by the Bluetooth Core Specification v5.3. + * The Controller `may` use the Skip value, and the implementation here + * covers a case where Skip value could lead to less events being + * listened to until Sync_Timeout. Listening to more consecutive events + * before Sync_Timeout increases probability of retaining the Periodic + * Synchronization. + */ + if (sync->timeout_reload > CONN_ESTAB_COUNTDOWN) { + uint16_t skip_max = sync->timeout_reload - CONN_ESTAB_COUNTDOWN; + + if (sync->skip > skip_max) { + sync->skip = skip_max; + } + } + + sync->sync_expire = CONN_ESTAB_COUNTDOWN; + + /* Extract the SCA value from the sca_chm field of the sync_info + * structure. + */ + sca = (si->sca_chm[PDU_SYNC_INFO_SCA_CHM_SCA_BYTE_OFFSET] & + PDU_SYNC_INFO_SCA_CHM_SCA_BIT_MASK) >> + PDU_SYNC_INFO_SCA_CHM_SCA_BIT_POS; + + lll->sca = sca; + + lll->window_widening_periodic_us = + DIV_ROUND_UP(((lll_clock_ppm_local_get() + + lll_clock_ppm_get(sca)) * + interval_us), USEC_PER_SEC); + lll->window_widening_max_us = (interval_us >> 1) - EVENT_IFS_US; + if (PDU_ADV_SYNC_INFO_OFFS_UNITS_GET(si)) { + lll->window_size_event_us = OFFS_UNIT_300_US; + } else { + lll->window_size_event_us = OFFS_UNIT_30_US; + } + +#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) + lll->node_cte_incomplete = NULL; +#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */ + + /* Prepare Periodic Advertising Sync Transfer Received event (dispatched later) */ + sync_handle = ull_sync_handle_get(sync); + rx = (void *)sync->node_rx_sync_estab; + rx->hdr.type = NODE_RX_TYPE_SYNC_TRANSFER_RECEIVED; + rx->hdr.handle = sync_handle; + rx->rx_ftr.param = sync; + + /* Create node_rx and assign values */ + se_past = (void *)rx->pdu; + se_past->rx_sync.status = BT_HCI_ERR_SUCCESS; + se_past->rx_sync.interval = interval; + se_past->rx_sync.phy = sync->lll.phy; + se_past->rx_sync.sca = sca; + se_past->conn_handle = ll_conn_handle_get(conn); + se_past->service_data = service_data; + + conn_interval_us = conn->lll.interval * CONN_INT_UNIT_US; + + /* Calculate offset and schedule sync radio events */ + ready_delay_us = lll_radio_rx_ready_delay_get(lll->phy, PHY_FLAGS_S8); + + sync_offset_us = PDU_ADV_SYNC_INFO_OFFSET_GET(si) * lll->window_size_event_us; + /* offs_adjust may be 1 only if sync setup by LL_PERIODIC_SYNC_IND */ + sync_offset_us += (PDU_ADV_SYNC_INFO_OFFS_ADJUST_GET(si) ? OFFS_ADJUST_US : 0U); + sync_offset_us -= EVENT_TICKER_RES_MARGIN_US; + sync_offset_us -= EVENT_JITTER_US; + sync_offset_us -= ready_delay_us; + + if (conn_evt_offset) { + int64_t conn_offset_us = (int64_t)conn_evt_offset * conn_interval_us; + + if ((int64_t)sync_offset_us + conn_offset_us < 0) { + uint32_t total_offset_us = abs((int64_t)sync_offset_us + conn_offset_us); + uint32_t sync_intervals = DIV_ROUND_UP(total_offset_us, interval_us); + + lll->event_counter += sync_intervals; + sync_offset_us = (sync_intervals * interval_us) - total_offset_us; + } else { + sync_offset_us += conn_offset_us; + } + } + + /* Calculate initial window widening - see Core Spec vol 6, part B, 5.1.13.1 */ + { + uint16_t event_delta; + uint32_t drift_us; + uint64_t da; + uint64_t db; + uint64_t d; + + const uint32_t local_sca_ppm = lll_clock_ppm_local_get(); + + event_delta = lll->event_counter - last_pa_event_counter; + + da = (uint64_t)(local_sca_ppm + lll_clock_ppm_get(sca)) * interval_us; + da = DIV_ROUND_UP(da * (uint64_t)event_delta, USEC_PER_SEC); + + db = (uint64_t)(local_sca_ppm + lll_clock_ppm_get(sender_sca)) * conn_interval_us; + db = DIV_ROUND_UP(db * (uint64_t)(ull_conn_event_counter(conn) - + sync_conn_event_count), USEC_PER_SEC); + + d = DIV_ROUND_UP((da + db) * (USEC_PER_SEC + local_sca_ppm + + lll_clock_ppm_get(sca) + + lll_clock_ppm_get(sender_sca)), USEC_PER_SEC); + + /* Limit drift compenstion to the maximum window widening */ + drift_us = MIN((uint32_t)d, lll->window_widening_max_us); + + /* Apply total drift to initial window size */ + lll->window_size_event_us += drift_us; + + /* Adjust offset if less than the drift compensation */ + while (sync_offset_us < drift_us) { + sync_offset_us += interval_us; + lll->event_counter++; + } + + sync_offset_us -= drift_us; + } + + interval_us -= lll->window_widening_periodic_us; + + /* Calculate event time reservation */ + slot_us = PDU_AC_MAX_US(PDU_AC_EXT_PAYLOAD_RX_SIZE, lll->phy); + slot_us += ready_delay_us; + + /* Add implementation defined radio event overheads */ + if (IS_ENABLED(CONFIG_BT_CTLR_EVENT_OVERHEAD_RESERVE_MAX)) { + slot_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + } + + /* TODO: active_to_start feature port */ + sync->ull.ticks_active_to_start = 0U; + sync->ull.ticks_prepare_to_start = + HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); + sync->ull.ticks_preempt_to_start = + HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US); + sync->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(slot_us); + + ticks_slot_offset = MAX(sync->ull.ticks_active_to_start, + sync->ull.ticks_prepare_to_start); + if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) { + ticks_slot_overhead = ticks_slot_offset; + } else { + ticks_slot_overhead = 0U; + } + ticks_slot_offset += HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US); + + sync->lll_sync_prepare = lll_sync_create_prepare; + + ticks_anchor = conn->llcp.prep.ticks_at_expire; + +#if defined(CONFIG_BT_PERIPHERAL) + if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL) { + /* Compensate for window widening */ + ticks_anchor += HAL_TICKER_US_TO_TICKS(conn->lll.periph.window_widening_event_us); + } +#endif /* CONFIG_BT_PERIPHERAL */ + + ret = ticker_start(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH, + (TICKER_ID_SCAN_SYNC_BASE + sync_handle), + ticks_anchor, + HAL_TICKER_US_TO_TICKS(sync_offset_us), + HAL_TICKER_US_TO_TICKS(interval_us), + HAL_TICKER_REMAINDER(interval_us), + TICKER_NULL_LAZY, + (sync->ull.ticks_slot + ticks_slot_overhead), + ticker_cb, sync, + ticker_start_op_cb, (void *)__LINE__); + LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || + (ret == TICKER_STATUS_BUSY)); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + + uint8_t ll_sync_create_cancel(void **rx) { struct ll_scan_set *scan_coded; @@ -353,7 +510,7 @@ uint8_t ll_sync_create_cancel(void **rx) /* It is safe to remove association with scanner as cancelled flag is * set, sync is_stop flag was set and sync has not been established. */ - ull_sync_setup_reset(scan); + ull_sync_setup_reset(sync); /* Mark the sync context as sync create cancelled */ if (IS_ENABLED(CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC)) { @@ -431,6 +588,22 @@ uint8_t ll_sync_terminate(uint16_t handle) LL_ASSERT(!aux->parent); } +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) + /* Clean up node_rx_sync_estab if still present */ + if (sync->node_rx_sync_estab) { + memq_link_t *link_sync_estab; + struct node_rx_pdu *node_rx; + + node_rx = (void *)sync->node_rx_sync_estab; + link_sync_estab = node_rx->hdr.link; + + ll_rx_link_release(link_sync_estab); + ll_rx_release(node_rx); + + sync->node_rx_sync_estab = NULL; + } +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + link_sync_lost = sync->node_rx_lost.rx.hdr.link; ll_rx_link_release(link_sync_lost); @@ -474,6 +647,111 @@ uint8_t ll_sync_recv_enable(uint16_t handle, uint8_t enable) return 0; } +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) +/* @brief Link Layer interface function corresponding to HCI LE Set Periodic + * Advertising Sync Transfer command. + * + * @param[in] conn_handle Connection_Handle identifying the connected device + * Range: 0x0000 to 0x0EFF. + * @param[in] service_data Service_Data value provided by the Host for use by the + * Host of the peer device. + * @param[in] sync_handle Sync_Handle identifying the periodic advertising + * train. Range: 0x0000 to 0x0EFF. + * + * @return HCI error codes as documented in Bluetooth Core Specification v5.4. + */ +uint8_t ll_sync_transfer(uint16_t conn_handle, uint16_t service_data, uint16_t sync_handle) +{ + struct ll_sync_set *sync; + struct ll_conn *conn; + + conn = ll_connected_get(conn_handle); + if (!conn) { + return BT_HCI_ERR_UNKNOWN_CONN_ID; + } + + /* Verify that sync_handle is valid */ + sync = ull_sync_is_enabled_get(sync_handle); + if (!sync) { + return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER; + } + + /* Call llcp to start LLCP_PERIODIC_SYNC_IND */ + return ull_cp_periodic_sync(conn, sync, NULL, service_data); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +/* @brief Link Layer interface function corresponding to HCI LE Set Periodic + * Advertising Sync Transfer Parameters command. + * + * @param[in] conn_handle Connection_Handle identifying the connected device + * Range: 0x0000 to 0x0EFF. + * @param[in] mode Mode specifies the action to be taken when a periodic advertising + * synchronization is received. + * @param[in] skip Skip specifying the number of consectutive periodic advertising + * packets that the receiver may skip after successfully reciving a + * periodic advertising packet. Range: 0x0000 to 0x01F3. + * @param[in] timeout Sync_timeout specifying the maximum permitted time between + * successful receives. Range: 0x000A to 0x4000. + * @param[in] cte_type CTE_Type specifying whether to only synchronize to periodic + * advertising with certain types of Constant Tone Extension. + * + * @return HCI error codes as documented in Bluetooth Core Specification v5.4. + */ +uint8_t ll_past_param(uint16_t conn_handle, uint8_t mode, uint16_t skip, uint16_t timeout, + uint8_t cte_type) +{ + struct ll_conn *conn; + + conn = ll_connected_get(conn_handle); + if (!conn) { + return BT_HCI_ERR_UNKNOWN_CONN_ID; + } + + if (mode == BT_HCI_LE_PAST_MODE_SYNC_FILTER_DUPLICATES && + !IS_ENABLED(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)) { + return BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL; + } + + /* Set PAST Param for connection instance */ + conn->past.mode = mode; + conn->past.skip = skip; + conn->past.timeout = timeout; + conn->past.cte_type = cte_type; + + return 0; +} + +/* @brief Link Layer interface function corresponding to HCI LE Set Default Periodic + * Advertising Sync Transfer Parameters command. + * + * @param[in] mode Mode specifies the action to be taken when a periodic advertising + * synchronization is received. + * @param[in] skip Skip specifying the number of consectutive periodic advertising + * packets that the receiver may skip after successfully reciving a + * periodic advertising packet. Range: 0x0000 to 0x01F3. + * @param[in] timeout Sync_timeout specifying the maximum permitted time between + * successful receives. Range: 0x000A to 0x4000. + * @param[in] cte_type CTE_Type specifying whether to only synchronize to periodic + * advertising with certain types of Constant Tone Extension. + * + * @return HCI error codes as documented in Bluetooth Core Specification v5.4. + */ +uint8_t ll_default_past_param(uint8_t mode, uint16_t skip, uint16_t timeout, uint8_t cte_type) +{ + if (mode == BT_HCI_LE_PAST_MODE_SYNC_FILTER_DUPLICATES && + !IS_ENABLED(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)) { + return BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL; + } + + /* Set default past param */ + ull_conn_default_past_param_set(mode, skip, timeout, cte_type); + + return 0; +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ + int ull_sync_init(void) { int err; @@ -594,8 +872,8 @@ void ull_sync_release(struct ll_sync_set *sync) mem_release(sync, &sync_free); } -void ull_sync_setup_addr_check(struct ll_scan_set *scan, uint8_t addr_type, - uint8_t *addr, uint8_t rl_idx) +void ull_sync_setup_addr_check(struct ll_sync_set *sync, struct ll_scan_set *scan, + uint8_t addr_type, uint8_t *addr, uint8_t rl_idx) { /* Check if Periodic Advertiser list to be used */ if (IS_ENABLED(CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST) && @@ -605,8 +883,8 @@ void ull_sync_setup_addr_check(struct ll_scan_set *scan, uint8_t addr_type, /* Remember the address, to check with * SID in Sync Info */ - scan->periodic.adv_addr_type = addr_type; - (void)memcpy(scan->periodic.adv_addr, addr, + sync->peer_id_addr_type = addr_type; + (void)memcpy(sync->peer_id_addr, addr, BDADDR_SIZE); /* Address matched */ @@ -615,22 +893,22 @@ void ull_sync_setup_addr_check(struct ll_scan_set *scan, uint8_t addr_type, /* Check in Resolving List */ } else if (IS_ENABLED(CONFIG_BT_CTLR_PRIVACY) && ull_filter_ull_pal_listed(rl_idx, &addr_type, - scan->periodic.adv_addr)) { + sync->peer_id_addr)) { /* Remember the address, to check with the * SID in Sync Info */ - scan->periodic.adv_addr_type = addr_type; + sync->peer_id_addr_type = addr_type; - /* Mark it as identity address from RPA (0x02, 0x03) */ - scan->periodic.adv_addr_type += 2U; + /* Mark it as identity address from RPA */ + sync->peer_addr_resolved = 1U; /* Address matched */ scan->periodic.state = LL_SYNC_STATE_ADDR_MATCH; } /* Check with explicitly supplied address */ - } else if ((addr_type == scan->periodic.adv_addr_type) && - !memcmp(addr, scan->periodic.adv_addr, BDADDR_SIZE)) { + } else if ((addr_type == sync->peer_id_addr_type) && + !memcmp(addr, sync->peer_id_addr, BDADDR_SIZE)) { /* Address matched */ scan->periodic.state = LL_SYNC_STATE_ADDR_MATCH; @@ -638,10 +916,10 @@ void ull_sync_setup_addr_check(struct ll_scan_set *scan, uint8_t addr_type, } else if (IS_ENABLED(CONFIG_BT_CTLR_PRIVACY) && (rl_idx < ll_rl_size_get())) { ll_rl_id_addr_get(rl_idx, &addr_type, addr); - if ((addr_type == scan->periodic.adv_addr_type) && - !memcmp(addr, scan->periodic.adv_addr, BDADDR_SIZE)) { - /* Mark it as identity address from RPA (0x02, 0x03) */ - scan->periodic.adv_addr_type += 2U; + if ((addr_type == sync->peer_id_addr_type) && + !memcmp(addr, sync->peer_id_addr, BDADDR_SIZE)) { + /* Mark it as identity address from RPA */ + sync->peer_addr_resolved = 1U; /* Identity address matched */ scan->periodic.state = LL_SYNC_STATE_ADDR_MATCH; @@ -649,15 +927,15 @@ void ull_sync_setup_addr_check(struct ll_scan_set *scan, uint8_t addr_type, } } -bool ull_sync_setup_sid_match(struct ll_scan_set *scan, uint8_t sid) +bool ull_sync_setup_sid_match(struct ll_sync_set *sync, struct ll_scan_set *scan, uint8_t sid) { return (scan->periodic.state == LL_SYNC_STATE_ADDR_MATCH) && ((IS_ENABLED(CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST) && scan->periodic.filter_policy && - ull_filter_ull_pal_match(scan->periodic.adv_addr_type, - scan->periodic.adv_addr, sid)) || + ull_filter_ull_pal_match(sync->peer_id_addr_type, + sync->peer_id_addr, sid)) || (!scan->periodic.filter_policy && - (sid == scan->periodic.sid))); + (sid == sync->sid))); } void ull_sync_setup(struct ll_scan_set *scan, struct ll_scan_aux_set *aux, @@ -705,18 +983,6 @@ void ull_sync_setup(struct ll_scan_set *scan, struct ll_scan_aux_set *aux, return; } -#if defined(CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC) || \ - defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT) - /* Remember the peer address. - * NOTE: Peer identity address is copied here when privacy is enable. - */ - sync->peer_id_addr_type = scan->periodic.adv_addr_type & 0x01; - (void)memcpy(sync->peer_id_addr, scan->periodic.adv_addr, - sizeof(sync->peer_id_addr)); -#endif /* CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC || - * CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT - */ - memcpy(lll->access_addr, si->aa, sizeof(lll->access_addr)); lll->data_chan_id = lll_chan_id(lll->access_addr); memcpy(lll->crc_init, si->crc_init, sizeof(lll->crc_init)); @@ -727,7 +993,12 @@ void ull_sync_setup(struct ll_scan_set *scan, struct ll_scan_aux_set *aux, interval = sys_le16_to_cpu(si->interval); interval_us = interval * PERIODIC_INT_UNIT_US; - /* Convert from 10ms units to interval units */ +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) + /* Save Periodic Advertisement Interval */ + sync->interval = interval; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ + + /* Convert fromm 10ms units to interval units */ sync->timeout_reload = RADIO_SYNC_EVENTS((sync->timeout * 10U * USEC_PER_MSEC), interval_us); @@ -803,7 +1074,7 @@ void ull_sync_setup(struct ll_scan_set *scan, struct ll_scan_aux_set *aux, rx = (void *)sync->node_rx_sync_estab; rx->hdr.type = NODE_RX_TYPE_SYNC; rx->hdr.handle = sync_handle; - rx->rx_ftr.param = scan; + rx->rx_ftr.param = sync; se = (void *)rx->pdu; se->interval = interval; se->phy = lll->phy; @@ -813,7 +1084,7 @@ void ull_sync_setup(struct ll_scan_set *scan, struct ll_scan_aux_set *aux, ftr = &node_rx->rx_ftr; pdu = (void *)((struct node_rx_pdu *)node_rx)->pdu; - ready_delay_us = lll_radio_rx_ready_delay_get(lll->phy, 1); + ready_delay_us = lll_radio_rx_ready_delay_get(lll->phy, PHY_FLAGS_S8); sync_offset_us = ftr->radio_end_us; sync_offset_us += PDU_ADV_SYNC_INFO_OFFSET_GET(si) * @@ -885,9 +1156,13 @@ void ull_sync_setup(struct ll_scan_set *scan, struct ll_scan_aux_set *aux, (ret == TICKER_STATUS_BUSY)); } -void ull_sync_setup_reset(struct ll_scan_set *scan) +void ull_sync_setup_reset(struct ll_sync_set *sync) { + struct ll_scan_set *scan; + /* Remove the sync context from being associated with scan contexts */ + scan = ull_scan_set_get(SCAN_HANDLE_1M); + scan->periodic.sync = NULL; #if defined(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST) @@ -895,14 +1170,7 @@ void ull_sync_setup_reset(struct ll_scan_set *scan) #endif /* CONFIG_BT_CTLR_FILTER_ACCEPT_LIST */ if (IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED)) { - struct ll_scan_set *scan_1m; - - scan_1m = ull_scan_set_get(SCAN_HANDLE_1M); - if (scan == scan_1m) { - scan = ull_scan_set_get(SCAN_HANDLE_PHY_CODED); - } else { - scan = scan_1m; - } + scan = ull_scan_set_get(SCAN_HANDLE_PHY_CODED); scan->periodic.sync = NULL; @@ -965,8 +1233,7 @@ void ull_sync_established_report(memq_link_t *link, struct node_rx_pdu *rx) #endif /* !CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING */ /* Prepare and dispatch sync notification */ - rx_establ = sync->node_rx_sync_estab; - rx_establ->hdr.type = NODE_RX_TYPE_SYNC; + rx_establ = (void *)sync->node_rx_sync_estab; rx_establ->hdr.handle = ull_sync_handle_get(sync); se = (void *)rx_establ->pdu; /* Clear the node to mark the sync establish as being completed. @@ -1297,6 +1564,110 @@ static inline struct ll_sync_set *sync_acquire(void) return mem_acquire(&sync_free); } +static struct ll_sync_set *ull_sync_create(uint8_t sid, uint16_t timeout, uint16_t skip, + uint8_t cte_type, uint8_t rx_enable, uint8_t nodups) +{ + memq_link_t *link_sync_estab; + memq_link_t *link_sync_lost; + struct node_rx_pdu *node_rx; + struct lll_sync *lll; + struct ll_sync_set *sync; + + link_sync_estab = ll_rx_link_alloc(); + if (!link_sync_estab) { + return NULL; + } + + link_sync_lost = ll_rx_link_alloc(); + if (!link_sync_lost) { + ll_rx_link_release(link_sync_estab); + + return NULL; + } + + node_rx = ll_rx_alloc(); + if (!node_rx) { + ll_rx_link_release(link_sync_lost); + ll_rx_link_release(link_sync_estab); + + return NULL; + } + + sync = sync_acquire(); + if (!sync) { + ll_rx_release(node_rx); + ll_rx_link_release(link_sync_lost); + ll_rx_link_release(link_sync_estab); + + return NULL; + } + + sync->peer_addr_resolved = 0U; + + /* Initialize sync context */ + node_rx->hdr.link = link_sync_estab; + sync->node_rx_lost.rx.hdr.link = link_sync_lost; + + /* Make sure that the node_rx_sync_establ hasn't got anything assigned. It is used to + * mark when sync establishment is in progress. + */ + LL_ASSERT(!sync->node_rx_sync_estab); + sync->node_rx_sync_estab = node_rx; + + /* Reporting initially enabled/disabled */ + sync->rx_enable = rx_enable; + +#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT) + sync->nodups = nodups; +#endif + sync->skip = skip; + sync->is_stop = 0U; + +#if defined(CONFIG_BT_CTLR_SYNC_ISO) + sync->enc = 0U; +#endif /* CONFIG_BT_CTLR_SYNC_ISO */ + + /* NOTE: Use timeout not zero to represent sync context used for sync + * create. + */ + sync->timeout = timeout; + + /* NOTE: Use timeout_reload not zero to represent sync established. */ + sync->timeout_reload = 0U; + sync->timeout_expire = 0U; + + /* Remember the SID */ + sync->sid = sid; + +#if defined(CONFIG_BT_CTLR_SYNC_ISO) + /* Reset Broadcast Isochronous Group Sync Establishment */ + sync->iso.sync_iso = NULL; +#endif /* CONFIG_BT_CTLR_SYNC_ISO */ + + /* Initialize sync LLL context */ + lll = &sync->lll; + lll->lll_aux = NULL; + lll->is_rx_enabled = sync->rx_enable; + lll->skip_prepare = 0U; + lll->skip_event = 0U; + lll->window_widening_prepare_us = 0U; + lll->window_widening_event_us = 0U; +#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING) + lll->cte_type = cte_type; +#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING */ + +#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) + ull_df_sync_cfg_init(&lll->df_cfg); + LL_ASSERT(!lll->node_cte_incomplete); +#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */ + + /* Initialise ULL and LLL headers */ + ull_hdr_init(&sync->ull); + lll_hdr_init(lll, sync); + + return sync; +} + static void sync_ticker_cleanup(struct ll_sync_set *sync, ticker_op_func stop_op_cb) { uint16_t sync_handle = ull_sync_handle_get(sync); @@ -1388,7 +1759,6 @@ static void sync_expire(void *param) /* Generate Periodic advertising sync failed to establish */ rx = (void *)sync->node_rx_sync_estab; - rx->hdr.type = NODE_RX_TYPE_SYNC; rx->hdr.handle = LLL_HANDLE_INVALID; /* Clear the node to mark the sync establish as being completed. @@ -1535,3 +1905,61 @@ static struct pdu_cte_info *pdu_cte_info_get(struct pdu_adv *pdu) return (struct pdu_cte_info *)hdr->data; } #endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING && !CONFIG_BT_CTLR_CTEINLINE_SUPPORT */ + +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER) +void ull_sync_transfer_received(struct ll_conn *conn, uint16_t service_data, + struct pdu_adv_sync_info *si, uint16_t conn_event_count, + uint16_t last_pa_event_counter, uint8_t sid, + uint8_t addr_type, uint8_t sca, uint8_t phy, + uint8_t *adv_addr, uint16_t sync_conn_event_count, + uint8_t addr_resolved) +{ + struct ll_sync_set *sync; + uint16_t conn_evt_current; + uint8_t rx_enable; + uint8_t nodups; + + if (conn->past.mode == BT_HCI_LE_PAST_MODE_NO_SYNC) { + /* Ignore LL_PERIODIC_SYNC_IND - see Bluetooth Core Specification v5.4 + * Vol 6, Part E, Section 7.8.91 + */ + return; + } + +#if defined(CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC) + /* Do not sync twice to the same peer and same SID */ + if (peer_sid_sync_exists(addr_type, adv_addr, sid)) { + return; + } +#endif /* CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC */ + + nodups = (conn->past.mode == BT_HCI_LE_PAST_MODE_SYNC_FILTER_DUPLICATES) ? 1U : 0U; + rx_enable = (conn->past.mode == BT_HCI_LE_PAST_MODE_NO_REPORTS) ? 0U : 1U; + + sync = ull_sync_create(sid, conn->past.timeout, conn->past.skip, conn->past.cte_type, + rx_enable, nodups); + if (!sync) { + return; + } + +#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING) + /* Reset filter policy in lll_sync */ + sync->lll.filter_policy = 0U; +#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING */ + + sync->peer_id_addr_type = addr_type; + sync->peer_addr_resolved = addr_resolved; + memcpy(sync->peer_id_addr, adv_addr, BDADDR_SIZE); + sync->lll.phy = phy; + + conn_evt_current = ull_conn_event_counter(conn); + + /* LLCP should have ensured this holds */ + LL_ASSERT(sync_conn_event_count != conn_evt_current); + + ull_sync_setup_from_sync_transfer(conn, service_data, sync, si, + conn_event_count - conn_evt_current, + last_pa_event_counter, sync_conn_event_count, + sca); +} +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_sync_internal.h b/subsys/bluetooth/controller/ll_sw/ull_sync_internal.h index dacc48ccdc7a93..a9d19c2e5466fe 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_sync_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_sync_internal.h @@ -9,15 +9,25 @@ int ull_sync_reset(void); uint16_t ull_sync_handle_get(struct ll_sync_set *sync); struct ll_sync_set *ull_sync_is_enabled_get(uint16_t handle); void ull_sync_release(struct ll_sync_set *sync); -void ull_sync_setup_addr_check(struct ll_scan_set *scan, uint8_t addr_type, - uint8_t *addr, uint8_t rl_idx); -bool ull_sync_setup_sid_match(struct ll_scan_set *scan, uint8_t sid); +void ull_sync_setup_addr_check(struct ll_sync_set *sync, struct ll_scan_set *scan, + uint8_t addr_type, uint8_t *addr, uint8_t rl_idx); +bool ull_sync_setup_sid_match(struct ll_sync_set *sync, struct ll_scan_set *scan, uint8_t sid); +void ull_sync_create_from_sync_transfer(uint16_t conn_handle, uint16_t service_data, + struct ll_sync_set *sync, + struct pdu_adv_sync_info *si, + uint32_t conn_offset_us); void ull_sync_setup(struct ll_scan_set *scan, struct ll_scan_aux_set *aux, struct node_rx_pdu *node_rx, struct pdu_adv_sync_info *si); -void ull_sync_setup_reset(struct ll_scan_set *scan); +void ull_sync_setup_reset(struct ll_sync_set *sync); void ull_sync_established_report(memq_link_t *link, struct node_rx_pdu *rx); void ull_sync_done(struct node_rx_event_done *done); void ull_sync_chm_update(uint8_t sync_handle, uint8_t *acad, uint8_t acad_len); int ull_sync_slot_update(struct ll_sync_set *sync, uint32_t slot_plus_us, uint32_t slot_minus_us); struct ll_sync_set *ull_sync_is_valid_get(struct ll_sync_set *sync); +void ull_sync_transfer_received(struct ll_conn *conn, uint16_t service_data, + struct pdu_adv_sync_info *si, uint16_t conn_event_count, + uint16_t last_pa_event_counter, uint8_t sid, + uint8_t addr_type, uint8_t sca, uint8_t phy, + uint8_t *adv_addr, uint16_t sync_conn_event_count, + uint8_t addr_resolved); diff --git a/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c b/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c index 5371c4bf9f84ba..fbe29c258afe57 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c @@ -31,18 +31,26 @@ #include "lll/lll_df_types.h" #include "lll_sync.h" #include "lll_sync_iso.h" +#include "lll_conn.h" +#include "lll_conn_iso.h" #include "isoal.h" +#include "ull_tx_queue.h" + #include "ull_scan_types.h" #include "ull_sync_types.h" #include "ull_iso_types.h" +#include "ull_conn_types.h" +#include "ull_conn_iso_types.h" #include "ull_internal.h" #include "ull_scan_internal.h" #include "ull_sync_internal.h" #include "ull_iso_internal.h" #include "ull_sync_iso_internal.h" +#include "ull_conn_internal.h" +#include "ull_conn_iso_internal.h" #include "ll.h" diff --git a/subsys/bluetooth/controller/ll_sw/ull_sync_types.h b/subsys/bluetooth/controller/ll_sw/ull_sync_types.h index 5a9c8786f7a6b2..d816abb217cbee 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_sync_types.h +++ b/subsys/bluetooth/controller/ll_sw/ull_sync_types.h @@ -32,13 +32,9 @@ struct ll_sync_set { */ void (*lll_sync_prepare)(void *param); -#if defined(CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC) || \ - defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT) uint8_t peer_id_addr[BDADDR_SIZE]; uint8_t peer_id_addr_type:1; -#endif /* CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC || - * CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT - */ + uint8_t peer_addr_resolved:1; uint8_t rx_enable:1; @@ -62,9 +58,7 @@ struct ll_sync_set { uint8_t num_bis : 5; #endif /* CONFIG_BT_CTLR_SYNC_ISO */ -#if defined(CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC) uint8_t sid; -#endif /* CONFIG_BT_CTLR_CHECK_SAME_PEER_SYNC */ /* node rx type with memory aligned storage for sync lost reason. * HCI will reference the value using the pdu member of @@ -94,8 +88,10 @@ struct ll_sync_set { #endif /* CONFIG_BT_CTLR_SYNC_ISO */ uint16_t data_len; +#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) + uint16_t interval; +#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ }; - struct node_rx_sync { uint8_t status; uint8_t phy; @@ -103,6 +99,12 @@ struct node_rx_sync { uint8_t sca; }; +struct node_rx_past_received { + struct node_rx_sync rx_sync; + uint16_t conn_handle; + uint16_t service_data; +}; + #if defined(CONFIG_BT_CTLR_SYNC_ISO) struct ll_sync_iso_set { struct ull_hdr ull; diff --git a/tests/bluetooth/df/connectionless_cte_rx/src/common.c b/tests/bluetooth/df/connectionless_cte_rx/src/common.c index cce56f1b8b6c34..ffa59f409d17f9 100644 --- a/tests/bluetooth/df/connectionless_cte_rx/src/common.c +++ b/tests/bluetooth/df/connectionless_cte_rx/src/common.c @@ -23,8 +23,12 @@ #include #include #include +#include +#include #include #include +#include +#include #include #include From fef5effd346ece9e84680eb561e8f59ab6930a55 Mon Sep 17 00:00:00 2001 From: Lucas Mathias Balling Date: Fri, 4 Oct 2024 13:32:36 +0200 Subject: [PATCH 4/5] tests: Bluetooth: bsim tests for Periodic Sync Transfer Implemented: * Central sync to broadcaster, connects to peripheral and transfers sync. Peripheral Syncs to PA * Broadcaster acting Central connects to peripheral and transfers sync. Peripheral sync to PA * Moved previous bis tests to test_bis.c and new PAST tests was added in test_past.c Signed-off-by: Lucas Mathias Balling --- tests/bsim/bluetooth/ll/bis/CMakeLists.txt | 14 +- tests/bsim/bluetooth/ll/bis/prj_past.conf | 33 + tests/bsim/bluetooth/ll/bis/src/main.c | 1238 +---------------- tests/bsim/bluetooth/ll/bis/src/test_bis.c | 1230 ++++++++++++++++ tests/bsim/bluetooth/ll/bis/src/test_past.c | 788 +++++++++++ tests/bsim/bluetooth/ll/bis/testcase.yaml | 11 + .../bluetooth/ll/bis/tests_scripts/past.sh | 29 + .../tests_scripts/past_default_past_params.sh | 30 + .../past_send_from_broadcaster.sh | 24 + tests/bsim/bluetooth/ll/compile.sh | 1 + 10 files changed, 2169 insertions(+), 1229 deletions(-) create mode 100644 tests/bsim/bluetooth/ll/bis/prj_past.conf create mode 100644 tests/bsim/bluetooth/ll/bis/src/test_bis.c create mode 100644 tests/bsim/bluetooth/ll/bis/src/test_past.c create mode 100755 tests/bsim/bluetooth/ll/bis/tests_scripts/past.sh create mode 100755 tests/bsim/bluetooth/ll/bis/tests_scripts/past_default_past_params.sh create mode 100755 tests/bsim/bluetooth/ll/bis/tests_scripts/past_send_from_broadcaster.sh diff --git a/tests/bsim/bluetooth/ll/bis/CMakeLists.txt b/tests/bsim/bluetooth/ll/bis/CMakeLists.txt index ba93504b318294..5af353802269c3 100644 --- a/tests/bsim/bluetooth/ll/bis/CMakeLists.txt +++ b/tests/bsim/bluetooth/ll/bis/CMakeLists.txt @@ -5,7 +5,19 @@ cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) project(bsim_test_bis) -target_sources(app PRIVATE src/main.c) +if(CONFIG_BT_CTLR_ISO) +target_sources(app PRIVATE + src/main.c + src/test_bis.c +) +endif() + +if(CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER) +target_sources(app PRIVATE + src/main.c + src/test_past.c +) +endif() zephyr_include_directories( ${ZEPHYR_BASE} diff --git a/tests/bsim/bluetooth/ll/bis/prj_past.conf b/tests/bsim/bluetooth/ll/bis/prj_past.conf new file mode 100644 index 00000000000000..589e965ff38083 --- /dev/null +++ b/tests/bsim/bluetooth/ll/bis/prj_past.conf @@ -0,0 +1,33 @@ +CONFIG_BT=y +CONFIG_BT_DEVICE_NAME="PAST" +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_EXT_ADV=y +CONFIG_BT_PER_ADV=y +CONFIG_BT_PER_ADV_SYNC=y +CONFIG_BT_CTLR_ADV_EXT=y + +CONFIG_BT_CTLR_TEST=y + +CONFIG_BT_ATT_PREPARE_COUNT=2 +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y +CONFIG_BT_DEVICE_NAME="bsim_test_split" +CONFIG_BT_L2CAP_TX_BUF_COUNT=6 + +CONFIG_BT_CTLR_PRIVACY=y + +# Periodic Advertising Sync Transfer Send/Receive functionality +CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER=y +CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER=y + +# Increase buffer size to get whole LLCP message sent in one +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=255 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_SIZE=251 + +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 + +CONFIG_BT_BROADCASTER=y diff --git a/tests/bsim/bluetooth/ll/bis/src/main.c b/tests/bsim/bluetooth/ll/bis/src/main.c index 1c8f72cd72b62d..a6a0b2fb89fc8e 100644 --- a/tests/bsim/bluetooth/ll/bis/src/main.c +++ b/tests/bsim/bluetooth/ll/bis/src/main.c @@ -1,1241 +1,23 @@ -/* main.c - Application main entry point */ - /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * Copyright (c) 2018 Oticon A/S * * SPDX-License-Identifier: Apache-2.0 */ -#include - -#include -#include -#include - -#include -#include -#include - -#include "subsys/bluetooth/host/hci_core.h" -#include "subsys/bluetooth/controller/include/ll.h" -#include "subsys/bluetooth/controller/util/memq.h" -#include "subsys/bluetooth/controller/ll_sw/lll.h" - -/* For VS data path */ -#include "subsys/bluetooth/controller/ll_sw/isoal.h" -#include "subsys/bluetooth/controller/ll_sw/ull_iso_types.h" - -#include "bs_types.h" -#include "bs_tracing.h" -#include "time_machine.h" #include "bstests.h" -#define FAIL(...) \ - do { \ - bst_result = Failed; \ - bs_trace_error_time_line(__VA_ARGS__); \ - } while (0) - -#define PASS(...) \ - do { \ - bst_result = Passed; \ - bs_trace_info_time(1, __VA_ARGS__); \ - } while (0) - -extern enum bst_result_t bst_result; - -static uint8_t mfg_data1[] = { 0xff, 0xff, 0x01, 0x02, 0x03, 0x04 }; -static uint8_t mfg_data2[] = { 0xff, 0xff, 0x05 }; - -static const struct bt_data per_ad_data1[] = { - BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data1, 6), -}; - -static const struct bt_data per_ad_data2[] = { - BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data2, 3), -}; - -static uint8_t chan_map[] = { 0x1F, 0XF1, 0x1F, 0xF1, 0x1F }; - -static bool volatile is_iso_connected; -static uint8_t volatile is_iso_disconnected; -static bool volatile deleting_pa_sync; - -#if !defined(CONFIG_TEST_LL_INTERFACE) -static void iso_connected(struct bt_iso_chan *chan); -static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason); -static void iso_recv(struct bt_iso_chan *chan, - const struct bt_iso_recv_info *info, struct net_buf *buf); - -static struct bt_iso_chan_ops iso_ops = { - .connected = iso_connected, - .disconnected = iso_disconnected, - .recv = iso_recv, -}; - -static struct bt_iso_chan_path iso_path_rx = { - .pid = BT_HCI_DATAPATH_ID_HCI -}; - -static struct bt_iso_chan_qos bis_iso_qos; -static struct bt_iso_chan_io_qos iso_tx_qos; -static struct bt_iso_chan_io_qos iso_rx_qos = { - .path = &iso_path_rx -}; - -static struct bt_iso_chan bis_iso_chan = { - .ops = &iso_ops, - .qos = &bis_iso_qos, -}; - -#define BIS_ISO_CHAN_COUNT 1 -static struct bt_iso_chan *bis_channels[BIS_ISO_CHAN_COUNT] = { &bis_iso_chan }; -static uint16_t seq_num; - -NET_BUF_POOL_FIXED_DEFINE(bis_tx_pool, BIS_ISO_CHAN_COUNT, - BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), - CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); - -#if defined(CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH) -static uint8_t test_rx_buffer[CONFIG_BT_CTLR_SYNC_ISO_PDU_LEN_MAX]; -static bool is_iso_vs_emitted; - -static isoal_status_t test_sink_sdu_alloc(const struct isoal_sink *sink_ctx, - const struct isoal_pdu_rx *valid_pdu, - struct isoal_sdu_buffer *sdu_buffer) -{ - sdu_buffer->dbuf = test_rx_buffer; - sdu_buffer->size = sizeof(test_rx_buffer); - - return ISOAL_STATUS_OK; -} - - -static isoal_status_t test_sink_sdu_emit(const struct isoal_sink *sink_ctx, - const struct isoal_emitted_sdu_frag *sdu_frag, - const struct isoal_emitted_sdu *sdu) -{ - printk("Vendor sink SDU fragment size %u / %u, seq_num %u, ts %u\n", - sdu_frag->sdu_frag_size, sdu->total_sdu_size, sdu_frag->sdu.sn, - sdu_frag->sdu.timestamp); - is_iso_vs_emitted = true; - - return ISOAL_STATUS_OK; -} - -static isoal_status_t test_sink_sdu_write(void *dbuf, - const size_t sdu_written, - const uint8_t *pdu_payload, - const size_t consume_len) -{ - memcpy((uint8_t *)dbuf + sdu_written, pdu_payload, consume_len); - - return ISOAL_STATUS_OK; -} - - -bool ll_data_path_sink_create(uint16_t handle, struct ll_iso_datapath *datapath, - isoal_sink_sdu_alloc_cb *sdu_alloc, - isoal_sink_sdu_emit_cb *sdu_emit, - isoal_sink_sdu_write_cb *sdu_write) -{ - ARG_UNUSED(handle); - ARG_UNUSED(datapath); - - *sdu_alloc = test_sink_sdu_alloc; - *sdu_emit = test_sink_sdu_emit; - *sdu_write = test_sink_sdu_write; - - printk("VS data path sink created\n"); - return true; -} -#endif /* CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH */ - -#define BUF_ALLOC_TIMEOUT_MS (30) /* milliseconds */ -NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT, - BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), - CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); - -static struct k_work_delayable iso_send_work; - -BUILD_ASSERT(sizeof(seq_num) <= CONFIG_BT_ISO_TX_MTU); - -static void iso_send(struct k_work *work) -{ - static uint8_t iso_data[CONFIG_BT_ISO_TX_MTU]; - static bool data_initialized; - struct net_buf *buf; - size_t iso_data_len; - int ret; - - if (!data_initialized) { - data_initialized = true; - - for (size_t i = 0; i < ARRAY_SIZE(iso_data); i++) { - iso_data[i] = (uint8_t)i; - } - } - - buf = net_buf_alloc(&tx_pool, K_MSEC(BUF_ALLOC_TIMEOUT_MS)); - if (!buf) { - FAIL("Data buffer allocate timeout on channel\n"); - return; - } - - net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE); - sys_put_le16(seq_num, iso_data); - iso_data_len = MAX(sizeof(seq_num), ((seq_num % CONFIG_BT_ISO_TX_MTU) + 1)); - net_buf_add_mem(buf, iso_data, iso_data_len); - - bs_trace_info_time(4, "ISO send: seq_num %u\n", seq_num); - ret = bt_iso_chan_send(&bis_iso_chan, buf, seq_num++); - if (ret < 0) { - FAIL("Unable to broadcast data on channel (%d)\n", ret); - net_buf_unref(buf); - return; - } - - k_work_schedule(&iso_send_work, K_USEC(9970)); -} -#endif /* !CONFIG_TEST_LL_INTERFACE */ - -static void setup_ext_adv(struct bt_le_ext_adv **adv) -{ - int err; - - printk("Create advertising set..."); - err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, NULL, adv); - if (err) { - FAIL("Failed to create advertising set (err %d)\n", err); - return; - } - printk("success.\n"); - - printk("Setting Periodic Advertising parameters..."); - err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_DEFAULT); - if (err) { - FAIL("Failed to set periodic advertising parameters (err %d)\n", - err); - return; - } - printk("success.\n"); - - printk("Enable Periodic Advertising..."); - err = bt_le_per_adv_start(*adv); - if (err) { - FAIL("Failed to enable periodic advertising (err %d)\n", err); - return; - } - printk("success.\n"); - - printk("Start extended advertising..."); - err = bt_le_ext_adv_start(*adv, BT_LE_EXT_ADV_START_DEFAULT); - if (err) { - printk("Failed to start extended advertising (err %d)\n", err); - return; - } - printk("success.\n"); -} - -static void teardown_ext_adv(struct bt_le_ext_adv *adv) -{ - int err; - - printk("Stop Periodic Advertising..."); - err = bt_le_per_adv_stop(adv); - if (err) { - FAIL("Failed to stop periodic advertising (err %d)\n", err); - return; - } - printk("success.\n"); - - printk("Stop Extended Advertising..."); - err = bt_le_ext_adv_stop(adv); - if (err) { - FAIL("Failed to stop extended advertising (err %d)\n", err); - return; - } - printk("success.\n"); - - printk("Deleting Extended Advertising..."); - err = bt_le_ext_adv_delete(adv); - if (err) { - FAIL("Failed to delete extended advertising (err %d)\n", err); - return; - } - printk("success.\n"); -} - -#if defined(CONFIG_TEST_LL_INTERFACE) -static void create_ll_big(uint8_t big_handle, struct bt_le_ext_adv *adv) -{ - uint16_t max_sdu = CONFIG_BT_CTLR_ADV_ISO_PDU_LEN_MAX; - uint8_t bcode[BT_ISO_BROADCAST_CODE_SIZE] = { 0 }; - uint32_t sdu_interval = 10000; /* us */ - uint16_t max_latency = 10; /* ms */ - uint8_t encryption = 0; - uint8_t bis_count = 1; /* TODO: Add support for multiple BIS per BIG */ - uint8_t phy = BIT(1); - uint8_t packing = 0; - uint8_t framing = 0; - uint8_t adv_handle; - uint8_t rtn = 0; - int err; - - printk("Creating LL BIG..."); - /* Assume that index == handle */ - adv_handle = bt_le_ext_adv_get_index(adv); - - err = ll_big_create(big_handle, adv_handle, bis_count, sdu_interval, - max_sdu, max_latency, rtn, phy, packing, framing, - encryption, bcode); - if (err) { - FAIL("Could not create BIG: %d\n", err); - return; - } - printk("success.\n"); -} - -static void terminate_ll_big(uint8_t big_handle) -{ - int err; - - printk("Terminating LL BIG..."); - err = ll_big_terminate(big_handle, BT_HCI_ERR_LOCALHOST_TERM_CONN); - if (err) { - FAIL("Could not terminate BIG: %d\n", err); - return; - } - printk("success.\n"); -} - -#else /* !CONFIG_TEST_LL_INTERFACE */ -static void create_big(struct bt_le_ext_adv *adv, struct bt_iso_big **big) -{ - struct bt_iso_big_create_param big_create_param = { 0 }; - int err; - - printk("Creating BIG...\n"); - big_create_param.bis_channels = bis_channels; - big_create_param.num_bis = BIS_ISO_CHAN_COUNT; - big_create_param.encryption = false; - big_create_param.interval = 10000; /* us */ - big_create_param.latency = 10; /* milliseconds */ - big_create_param.packing = 0; /* 0 - sequential; 1 - interleaved */ - big_create_param.framing = 0; /* 0 - unframed; 1 - framed */ - iso_tx_qos.sdu = CONFIG_BT_ISO_TX_MTU; /* bytes */ - iso_tx_qos.rtn = 2; - iso_tx_qos.phy = BT_GAP_LE_PHY_2M; - bis_iso_qos.tx = &iso_tx_qos; - bis_iso_qos.rx = NULL; - err = bt_iso_big_create(adv, &big_create_param, big); - if (err) { - FAIL("Could not create BIG: %d\n", err); - return; - } - printk("success.\n"); - - printk("Wait for ISO connected callback..."); - while (!is_iso_connected) { - k_sleep(K_MSEC(100)); - } - printk("ISO connected\n"); -} - -static void terminate_big(struct bt_iso_big *big) -{ - int err; - - printk("Terminating BIG...\n"); - err = bt_iso_big_terminate(big); - if (err) { - FAIL("Could not terminate BIG: %d\n", err); - return; - } - printk("success.\n"); - - printk("Wait for ISO disconnected callback..."); - while (is_iso_disconnected == 0U) { - k_sleep(K_MSEC(100)); - } - printk("ISO disconnected\n"); -} -#endif /* !CONFIG_TEST_LL_INTERFACE */ - -#if defined(CONFIG_BT_ISO_TEST_PARAMS) -static void create_advanced_big(struct bt_le_ext_adv *adv, struct bt_iso_big **big) -{ - struct bt_iso_big_create_param big_create_param; - int err; - - printk("Creating BIG...\n"); - big_create_param.bis_channels = bis_channels; - big_create_param.num_bis = BIS_ISO_CHAN_COUNT; - big_create_param.encryption = false; - big_create_param.interval = 10000; /* us */ - big_create_param.packing = 0; /* 0 - sequential; 1 - interleaved */ - big_create_param.framing = 0; /* 0 - unframed; 1 - framed */ - big_create_param.irc = BT_ISO_IRC_MIN; - big_create_param.pto = BT_ISO_PTO_MIN; - big_create_param.iso_interval = big_create_param.interval / 1250U; /* N * 10 ms */ - - iso_tx_qos.sdu = 502; /* bytes */ - iso_tx_qos.phy = BT_GAP_LE_PHY_2M; - iso_tx_qos.max_pdu = BT_ISO_PDU_MAX; - iso_tx_qos.burst_number = BT_ISO_BN_MIN; - - bis_iso_qos.tx = &iso_tx_qos; - bis_iso_qos.rx = NULL; - bis_iso_qos.num_subevents = BT_ISO_NSE_MIN; - - err = bt_iso_big_create(adv, &big_create_param, big); - if (err) { - FAIL("Could not create BIG: %d\n", err); - return; - } - printk("success.\n"); - - printk("Wait for ISO connected callback..."); - while (!is_iso_connected) { - k_sleep(K_MSEC(100)); - } - printk("ISO connected\n"); -} -#endif /* CONFIG_BT_ISO_TEST_PARAMS */ - -static void test_iso_main(void) -{ - struct bt_le_ext_adv *adv; - int err; - - printk("\n*ISO broadcast test*\n"); - - printk("Bluetooth initializing..."); - err = bt_enable(NULL); - if (err) { - FAIL("Could not init BT: %d\n", err); - return; - } - printk("success.\n"); - - setup_ext_adv(&adv); - -#if defined(CONFIG_TEST_LL_INTERFACE) - uint8_t big_handle = 0; - - create_ll_big(big_handle, adv); - -#else /* !CONFIG_TEST_LL_INTERFACE */ - struct bt_iso_big *big; - - create_big(adv, &big); - - k_work_init_delayable(&iso_send_work, iso_send); - k_work_schedule(&iso_send_work, K_NO_WAIT); -#endif /* !CONFIG_TEST_LL_INTERFACE */ - - k_sleep(K_MSEC(5000)); - - printk("Update periodic advertising data 1..."); - err = bt_le_per_adv_set_data(adv, per_ad_data1, - ARRAY_SIZE(per_ad_data1)); - if (err) { - FAIL("Failed to update periodic advertising data 1 (%d).\n", - err); - } - printk("success.\n"); - - k_sleep(K_MSEC(2500)); - - printk("Periodic Advertising and ISO Channel Map Update..."); - err = bt_le_set_chan_map(chan_map); - if (err) { - FAIL("Channel Map Update failed.\n"); - } - printk("success.\n"); - - k_sleep(K_MSEC(2500)); - - printk("Update periodic advertising data 2..."); - err = bt_le_per_adv_set_data(adv, per_ad_data2, - ARRAY_SIZE(per_ad_data2)); - if (err) { - FAIL("Failed to update periodic advertising data 2 (%d).\n", - err); - } - printk("success.\n"); - - k_sleep(K_MSEC(5000)); - -#if defined(CONFIG_TEST_LL_INTERFACE) - terminate_ll_big(big_handle); - -#else /* !CONFIG_TEST_LL_INTERFACE */ - k_work_cancel_delayable(&iso_send_work); - - terminate_big(big); - big = NULL; - -#if defined(CONFIG_BT_ISO_TEST_PARAMS) - /* Quick check to just verify that creating a BIG using advanced/test - * parameters work - */ - create_advanced_big(adv, &big); - - terminate_big(big); - big = NULL; -#endif /* CONFIG_BT_ISO_TEST_PARAMS */ -#endif /* !CONFIG_TEST_LL_INTERFACE */ - - k_sleep(K_MSEC(10000)); - - teardown_ext_adv(adv); - adv = NULL; - - PASS("ISO tests Passed\n"); - - return; -} - -static const char *phy2str(uint8_t phy) -{ - switch (phy) { - case 0: return "No packets"; - case BT_GAP_LE_PHY_1M: return "LE 1M"; - case BT_GAP_LE_PHY_2M: return "LE 2M"; - case BT_GAP_LE_PHY_CODED: return "LE Coded"; - default: return "Unknown"; - } -} - -#if !defined(CONFIG_TEST_LL_INTERFACE) -/** Print data as d_0 d_1 d_2 ... d_(n-2) d_(n-1) d_(n) to show the 3 first and 3 last octets - * - * Examples: - * 01 - * 0102 - * 010203 - * 01020304 - * 0102030405 - * 010203040506 - * 010203...050607 - * 010203...060708 - * etc. - */ -static void iso_print_data(uint8_t *data, size_t data_len) -{ - /* Maximum number of octets from each end of the data */ - const uint8_t max_octets = 3; - char data_str[35]; - size_t str_len; - - str_len = bin2hex(data, MIN(max_octets, data_len), data_str, sizeof(data_str)); - if (data_len > max_octets) { - if (data_len > (max_octets * 2)) { - static const char dots[] = "..."; - - strcat(&data_str[str_len], dots); - str_len += strlen(dots); - } - - str_len += bin2hex(data + (data_len - MIN(max_octets, data_len - max_octets)), - MIN(max_octets, data_len - max_octets), - data_str + str_len, - sizeof(data_str) - str_len); - } - - printk("\t %s\n", data_str); -} - -#define SEQ_NUM_MAX 1000U -static uint16_t expected_seq_num[CONFIG_BT_ISO_MAX_CHAN]; - -static void iso_recv(struct bt_iso_chan *chan, - const struct bt_iso_recv_info *info, struct net_buf *buf) -{ - uint16_t seq_num; - uint8_t index; - - index = bt_conn_index(chan->iso); - - printk("Incoming data channel %p (%u) flags 0x%x seq_num %u ts %u len %u:\n", - chan, index, info->flags, info->seq_num, info->ts, buf->len); - iso_print_data(buf->data, buf->len); - - seq_num = sys_get_le16(buf->data); - if (info->flags & BT_ISO_FLAGS_VALID) { - if (seq_num != expected_seq_num[index]) { - if (expected_seq_num[index]) { - FAIL("ISO data miss match, expected %u actual %u\n", - expected_seq_num[index], seq_num); - } - expected_seq_num[index] = seq_num; - } - - expected_seq_num[index]++; - - } else if (expected_seq_num[index] && - expected_seq_num[index] < SEQ_NUM_MAX) { - FAIL("%s: Invalid ISO data after valid ISO data reception.\n" - "Expected %u\n", __func__, expected_seq_num[index]); - } -} - -static void iso_connected(struct bt_iso_chan *chan) -{ - printk("ISO Channel %p connected\n", chan); - - seq_num = 0U; - is_iso_connected = true; -} - -static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason) -{ - printk("ISO Channel %p disconnected with reason 0x%02x\n", chan, reason); - - is_iso_disconnected = reason; -} -#endif /* !CONFIG_TEST_LL_INTERFACE */ - -static bool volatile is_sync; - -static void pa_sync_cb(struct bt_le_per_adv_sync *sync, - struct bt_le_per_adv_sync_synced_info *info) -{ - char le_addr[BT_ADDR_LE_STR_LEN]; - - bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); - - printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, " - "Interval 0x%04x (%u ms), PHY %s\n", - bt_le_per_adv_sync_get_index(sync), le_addr, - info->interval, info->interval * 5 / 4, phy2str(info->phy)); - - is_sync = true; -} - -static void pa_terminated_cb(struct bt_le_per_adv_sync *sync, - const struct bt_le_per_adv_sync_term_info *info) -{ - char le_addr[BT_ADDR_LE_STR_LEN]; - - bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); - - printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n", - bt_le_per_adv_sync_get_index(sync), le_addr); - - if (!deleting_pa_sync) { - FAIL("PA terminated unexpectedly\n"); - } else { - deleting_pa_sync = false; - } -} - -static bool volatile is_sync_recv; - -static void pa_recv_cb(struct bt_le_per_adv_sync *sync, - const struct bt_le_per_adv_sync_recv_info *info, - struct net_buf_simple *buf) -{ - char le_addr[BT_ADDR_LE_STR_LEN]; - - bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); - - printk("PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, " - "RSSI %i, CTE %u, data length %u\n", - bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power, - info->rssi, info->cte_type, buf->len); - - is_sync_recv = true; -} - -static void -pa_state_changed_cb(struct bt_le_per_adv_sync *sync, - const struct bt_le_per_adv_sync_state_info *info) -{ - printk("PER_ADV_SYNC[%u]: state changed, receive %s.\n", - bt_le_per_adv_sync_get_index(sync), - info->recv_enabled ? "enabled" : "disabled"); -} - -static bool volatile is_big_info; - -static void pa_biginfo_cb(struct bt_le_per_adv_sync *sync, - const struct bt_iso_biginfo *biginfo) -{ - char le_addr[BT_ADDR_LE_STR_LEN]; - - bt_addr_le_to_str(biginfo->addr, le_addr, sizeof(le_addr)); - printk("BIG INFO[%u]: [DEVICE]: %s, sid 0x%02x, " - "num_bis %u, nse %u, interval 0x%04x (%u ms), " - "bn %u, pto %u, irc %u, max_pdu %u, " - "sdu_interval %u us, max_sdu %u, phy %s, " - "%s framing, %sencrypted\n", - bt_le_per_adv_sync_get_index(sync), le_addr, biginfo->sid, - biginfo->num_bis, biginfo->sub_evt_count, - biginfo->iso_interval, - (biginfo->iso_interval * 5 / 4), - biginfo->burst_number, biginfo->offset, - biginfo->rep_count, biginfo->max_pdu, biginfo->sdu_interval, - biginfo->max_sdu, phy2str(biginfo->phy), - biginfo->framing ? "with" : "without", - biginfo->encryption ? "" : "not "); - - if (!is_big_info) { - is_big_info = true; - } -} - -static struct bt_le_per_adv_sync_cb sync_cb = { - .synced = pa_sync_cb, - .term = pa_terminated_cb, - .recv = pa_recv_cb, - .state_changed = pa_state_changed_cb, - .biginfo = pa_biginfo_cb, -}; - -#define NAME_LEN 30 - -static bool data_cb(struct bt_data *data, void *user_data) -{ - char *name = user_data; - - switch (data->type) { - case BT_DATA_NAME_SHORTENED: - case BT_DATA_NAME_COMPLETE: - memcpy(name, data->data, MIN(data->data_len, NAME_LEN - 1)); - return false; - default: - return true; - } -} - -static bool volatile is_periodic; -static bt_addr_le_t per_addr; -static uint8_t per_sid; - -static void scan_recv(const struct bt_le_scan_recv_info *info, - struct net_buf_simple *buf) -{ - char le_addr[BT_ADDR_LE_STR_LEN]; - char name[NAME_LEN]; - - (void)memset(name, 0, sizeof(name)); - - bt_data_parse(buf, data_cb, name); - - bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); - printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s " - "C:%u S:%u D:%u SR:%u E:%u Prim: %s, Secn: %s, " - "Interval: 0x%04x (%u ms), SID: %u\n", - le_addr, info->adv_type, info->tx_power, info->rssi, name, - (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0, - (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0, - (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0, - (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0, - (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0, - phy2str(info->primary_phy), phy2str(info->secondary_phy), - info->interval, info->interval * 5 / 4, info->sid); - - if (info->interval) { - if (!is_periodic) { - is_periodic = true; - per_sid = info->sid; - bt_addr_le_copy(&per_addr, info->addr); - } - } -} - -static struct bt_le_scan_cb scan_callbacks = { - .recv = scan_recv, -}; - -static void test_iso_recv_main(void) -{ - struct bt_le_scan_param scan_param = { - .type = BT_LE_SCAN_TYPE_ACTIVE, - .options = BT_LE_SCAN_OPT_NONE, - .interval = 0x0004, - .window = 0x0004, - }; - struct bt_le_per_adv_sync_param sync_create_param; - struct bt_le_per_adv_sync *sync = NULL; - int err; - - printk("\n*ISO broadcast test*\n"); - - printk("Bluetooth initializing..."); - err = bt_enable(NULL); - if (err) { - FAIL("Could not init BT: %d\n", err); - return; - } - printk("success.\n"); - - printk("Scan callbacks register..."); - bt_le_scan_cb_register(&scan_callbacks); - printk("success.\n"); - - printk("Periodic Advertising callbacks register..."); - bt_le_per_adv_sync_cb_register(&sync_cb); - printk("Success.\n"); - - printk("Start scanning..."); - is_periodic = false; - err = bt_le_scan_start(&scan_param, NULL); - if (err) { - FAIL("Could not start scan: %d\n", err); - return; - } - printk("success.\n"); - - while (!is_periodic) { - k_sleep(K_MSEC(100)); - } - printk("Periodic Advertising found (SID: %u)\n", per_sid); - - printk("Creating Periodic Advertising Sync..."); - is_sync = false; - bt_addr_le_copy(&sync_create_param.addr, &per_addr); - sync_create_param.options = - BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED; - sync_create_param.sid = per_sid; - sync_create_param.skip = 0; - sync_create_param.timeout = 0xa; - err = bt_le_per_adv_sync_create(&sync_create_param, &sync); - if (err) { - FAIL("Could not create sync: %d\n", err); - return; - } - printk("success.\n"); - - /* TODO: Enable when advertiser is added */ - printk("Waiting for sync..."); - while (!is_sync) { - k_sleep(K_MSEC(100)); - } - - printk("Stop scanning..."); - err = bt_le_scan_stop(); - if (err) { - FAIL("Could not stop scan: %d\n", err); - return; - } - printk("success.\n"); - - printk("Wait for BIG Info Advertising Report..."); - is_big_info = false; - while (!is_big_info) { - k_sleep(K_MSEC(100)); - } - printk("success.\n"); - -#if defined(CONFIG_TEST_LL_INTERFACE) - uint8_t bcode[BT_ISO_BROADCAST_CODE_SIZE] = { 0 }; - uint16_t sync_timeout = 10; - uint8_t bis[1] = { 0x01, }; - uint8_t big_handle = 0; - uint8_t encryption = 0; - uint8_t mse = 0; - - printk("Creating BIG Sync..."); - err = ll_big_sync_create(big_handle, sync->handle, encryption, bcode, - mse, sync_timeout, ARRAY_SIZE(bis), bis); - if (err) { - FAIL("Could not create BIG sync: %d\n", err); - return; - } - printk("success.\n"); - - k_sleep(K_MSEC(5000)); - - printk("Terminating BIG Sync..."); - struct node_rx_pdu *node_rx = NULL; - err = ll_big_sync_terminate(big_handle, (void **)&node_rx); - if (err) { - FAIL("Could not terminate BIG sync: %d\n", err); - return; - } - printk("success.\n"); - - if (node_rx) { - FAIL("Generated Node Rx for synchronized BIG.\n"); - } - - k_sleep(K_MSEC(5000)); - - printk("Creating BIG Sync after terminate..."); - err = ll_big_sync_create(big_handle, sync->handle, encryption, bcode, - mse, sync_timeout, ARRAY_SIZE(bis), bis); - if (err) { - FAIL("Could not create BIG sync: %d\n", err); - return; - } - printk("success.\n"); - - printk("Terminating BIG Sync..."); - node_rx = NULL; - err = ll_big_sync_terminate(big_handle, (void **)&node_rx); - if (err) { - FAIL("Could not terminate BIG sync: %d\n", err); - return; - } - printk("success.\n"); - - if (node_rx) { - node_rx->hdr.next = NULL; - ll_rx_mem_release((void **)&node_rx); - } - - printk("Deleting Periodic Advertising Sync..."); - deleting_pa_sync = true; - err = bt_le_per_adv_sync_delete(sync); - if (err) { - FAIL("Failed to delete periodic advertising sync (err %d)\n", - err); - return; - } - printk("success.\n"); - -#else /* !CONFIG_TEST_LL_INTERFACE */ - struct bt_iso_big_sync_param big_param = { 0, }; - struct bt_iso_big *big; - - printk("ISO BIG create sync..."); - is_iso_connected = false; - bis_iso_qos.tx = NULL; - bis_iso_qos.rx = &iso_rx_qos; - big_param.bis_channels = bis_channels; - big_param.num_bis = BIS_ISO_CHAN_COUNT; - big_param.bis_bitfield = BT_ISO_BIS_INDEX_BIT(1); /* BIS 1 selected */ - big_param.mse = 1; - big_param.sync_timeout = 100; /* 1000 ms */ - big_param.encryption = false; - iso_path_rx.pid = BT_HCI_DATAPATH_ID_HCI; - memset(big_param.bcode, 0, sizeof(big_param.bcode)); - err = bt_iso_big_sync(sync, &big_param, &big); - if (err) { - FAIL("Could not create BIG sync: %d\n", err); - return; - } - printk("success.\n"); - - printk("Wait for ISO connected callback..."); - while (!is_iso_connected) { - k_sleep(K_MSEC(100)); - } - - printk("ISO terminate BIG..."); - is_iso_disconnected = 0U; - err = bt_iso_big_terminate(big); - if (err) { - FAIL("Could not terminate BIG sync: %d\n", err); - return; - } - printk("success.\n"); - - printk("Waiting for ISO disconnected callback...\n"); - while (!is_iso_disconnected) { - k_sleep(K_MSEC(100)); - } - printk("disconnected.\n"); - - if (is_iso_disconnected != BT_HCI_ERR_LOCALHOST_TERM_CONN) { - FAIL("Local Host Terminate Failed.\n"); - } - - printk("ISO BIG create sync (test remote disconnect)..."); - is_iso_connected = false; - is_iso_disconnected = 0U; - memset(expected_seq_num, 0U, sizeof(expected_seq_num)); - err = bt_iso_big_sync(sync, &big_param, &big); - if (err) { - FAIL("Could not create BIG sync: %d\n", err); - return; - } - printk("success.\n"); - - printk("Wait for ISO connected callback..."); - while (!is_iso_connected) { - k_sleep(K_MSEC(100)); - } - printk("connected.\n"); - - printk("Waiting for ISO disconnected callback...\n"); - while (!is_iso_disconnected) { - k_sleep(K_MSEC(100)); - } - printk("disconnected.\n"); - - if (is_iso_disconnected != BT_HCI_ERR_REMOTE_USER_TERM_CONN) { - FAIL("Remote Host Terminate Failed.\n"); - } - - printk("Periodic sync receive enable...\n"); - err = bt_le_per_adv_sync_recv_enable(sync); - if (err) { - printk("failed (err %d)\n", err); - return; - } - printk("receive enabled.\n"); - - uint8_t check_countdown = 3; - - printk("Waiting for remote BIG terminate by checking for missing " - "%u BIG Info report...\n", check_countdown); - do { - is_sync_recv = false; - is_big_info = false; - while (!is_sync_recv) { - k_sleep(K_MSEC(100)); - } - - k_sleep(K_MSEC(100)); - - if (!is_big_info) { - if (!--check_countdown) { - break; - } - } - } while (1); - printk("success.\n"); - - printk("Deleting Periodic Advertising Sync..."); - deleting_pa_sync = true; - err = bt_le_per_adv_sync_delete(sync); - if (err) { - FAIL("Failed to delete periodic advertising sync (err %d)\n", - err); - return; - } - printk("success.\n"); - - for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { - if (expected_seq_num[chan] < SEQ_NUM_MAX) { - FAIL("ISO Data reception incomplete %u (%u).\n", - expected_seq_num[chan], SEQ_NUM_MAX); - return; - } - } -#endif /* !CONFIG_TEST_LL_INTERFACE */ - - PASS("ISO recv test Passed\n"); - - return; -} - -#if defined(CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH) -static void test_iso_recv_vs_dp_main(void) -{ - struct bt_le_scan_param scan_param = { - .type = BT_LE_SCAN_TYPE_ACTIVE, - .options = BT_LE_SCAN_OPT_NONE, - .interval = 0x0004, - .window = 0x0004, - }; - struct bt_le_per_adv_sync_param sync_create_param; - struct bt_le_per_adv_sync *sync = NULL; - int err; - - printk("Bluetooth initializing... "); - err = bt_enable(NULL); - if (err) { - FAIL("Could not init BT: %d\n", err); - return; - } - printk("success.\n"); - - printk("Scan callbacks register... "); - bt_le_scan_cb_register(&scan_callbacks); - printk("success.\n"); - - printk("Periodic Advertising callbacks register... "); - bt_le_per_adv_sync_cb_register(&sync_cb); - printk("success.\n"); - - printk("Configure vendor data path... "); - err = bt_configure_data_path(BT_HCI_DATAPATH_DIR_CTLR_TO_HOST, - BT_HCI_DATAPATH_ID_VS, 0U, NULL); - if (err) { - FAIL("Failed (err %d)\n", err); - return; - } - - printk("success.\n"); - printk("Start scanning... "); - is_periodic = false; - err = bt_le_scan_start(&scan_param, NULL); - if (err) { - FAIL("Could not start scan: %d\n", err); - return; - } - printk("success.\n"); - - while (!is_periodic) { - k_sleep(K_MSEC(100)); - } - printk("Periodic Advertising found (SID: %u)\n", per_sid); - - printk("Creating Periodic Advertising Sync... "); - is_sync = false; - - bt_addr_le_copy(&sync_create_param.addr, &per_addr); - sync_create_param.options = - BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED; - sync_create_param.sid = per_sid; - sync_create_param.skip = 0; - sync_create_param.timeout = 0xa; - - err = bt_le_per_adv_sync_create(&sync_create_param, &sync); - if (err) { - FAIL("Could not create sync: %d\n", err); - return; - } - printk("success.\n"); - - /* TODO: Enable when advertiser is added */ - printk("Waiting for sync...\n"); - while (!is_sync) { - k_sleep(K_MSEC(100)); - } - printk("success.\n"); - - printk("Stop scanning... "); - err = bt_le_scan_stop(); - if (err) { - FAIL("Could not stop scan: %d\n", err); - return; - } - printk("success.\n"); - - printk("Wait for BIG Info Advertising Report...\n"); - is_big_info = false; - while (!is_big_info) { - k_sleep(K_MSEC(100)); - } - printk("success.\n"); - - struct bt_iso_big_sync_param big_param = { 0, }; - struct bt_iso_big *big; - - printk("ISO BIG create sync... "); - is_iso_connected = false; - bis_iso_qos.tx = NULL; - bis_iso_qos.rx = &iso_rx_qos; - big_param.bis_channels = bis_channels; - big_param.num_bis = BIS_ISO_CHAN_COUNT; - big_param.bis_bitfield = BT_ISO_BIS_INDEX_BIT(1); /* BIS 1 selected */ - big_param.mse = 1; - big_param.sync_timeout = 100; /* 1000 ms */ - big_param.encryption = false; - memset(big_param.bcode, 0, sizeof(big_param.bcode)); - - is_iso_connected = false; - is_iso_disconnected = 0U; - is_iso_vs_emitted = false; - iso_path_rx.pid = BT_HCI_DATAPATH_ID_VS; - - err = bt_iso_big_sync(sync, &big_param, &big); - if (err) { - FAIL("Could not create BIG sync: %d\n", err); - return; - } - printk("success.\n"); - - printk("Wait for ISO connected callback... "); - while (!is_iso_connected) { - k_sleep(K_MSEC(100)); - } - - /* Allow some SDUs to be received */ - k_sleep(K_MSEC(100)); - - printk("ISO terminate BIG... "); - is_iso_disconnected = 0U; - err = bt_iso_big_terminate(big); - if (err) { - FAIL("Could not terminate BIG sync: %d\n", err); - return; - } - printk("success.\n"); - - printk("Waiting for ISO disconnected callback...\n"); - while (!is_iso_disconnected) { - k_sleep(K_MSEC(100)); - } - printk("disconnected.\n"); - - if (is_iso_disconnected != BT_HCI_ERR_LOCALHOST_TERM_CONN) { - FAIL("Local Host Terminate Failed.\n"); - } - - if (!is_iso_vs_emitted) { - FAIL("Emitting of VS SDUs failed.\n"); - } - - printk("success.\n"); - - printk("Deleting Periodic Advertising Sync... "); - deleting_pa_sync = true; - err = bt_le_per_adv_sync_delete(sync); - if (err) { - FAIL("Failed to delete periodic advertising sync (err %d)\n", - err); - return; - } - printk("success.\n"); - - PASS("ISO recv VS test Passed\n"); -} -#endif /* CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH */ - -static void test_iso_init(void) -{ - bst_ticker_set_next_tick_absolute(60e6); - bst_result = In_progress; -} - -static void test_iso_tick(bs_time_t HW_device_time) -{ - if (bst_result != Passed) { - FAIL("test failed (not passed after seconds)\n"); - } -} - -static const struct bst_test_instance test_def[] = { - { - .test_id = "broadcast", - .test_descr = "ISO broadcast", - .test_pre_init_f = test_iso_init, - .test_tick_f = test_iso_tick, - .test_main_f = test_iso_main - }, - { - .test_id = "receive", - .test_descr = "ISO receive", - .test_pre_init_f = test_iso_init, - .test_tick_f = test_iso_tick, - .test_main_f = test_iso_recv_main - }, -#if defined(CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH) - { - .test_id = "receive_vs_dp", - .test_descr = "ISO receive VS", - .test_pre_init_f = test_iso_init, - .test_tick_f = test_iso_tick, - .test_main_f = test_iso_recv_vs_dp_main - }, -#endif /* CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH */ - BSTEST_END_MARKER -}; - -struct bst_test_list *test_iso_install(struct bst_test_list *tests) -{ - return bst_add_tests(tests, test_def); -} +#ifdef CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER +extern struct bst_test_list *test_past_install(struct bst_test_list *tests); +#else +extern struct bst_test_list *test_iso_install(struct bst_test_list *tests); +#endif /* CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER */ bst_test_install_t test_installers[] = { +#ifdef CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER + test_past_install, +#else test_iso_install, +#endif /* CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER */ NULL }; diff --git a/tests/bsim/bluetooth/ll/bis/src/test_bis.c b/tests/bsim/bluetooth/ll/bis/src/test_bis.c new file mode 100644 index 00000000000000..3b2b1a001c25cd --- /dev/null +++ b/tests/bsim/bluetooth/ll/bis/src/test_bis.c @@ -0,0 +1,1230 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#include +#include +#include + +#include "subsys/bluetooth/host/hci_core.h" +#include "subsys/bluetooth/controller/include/ll.h" +#include "subsys/bluetooth/controller/util/memq.h" +#include "subsys/bluetooth/controller/ll_sw/lll.h" + +/* For VS data path */ +#include "subsys/bluetooth/controller/ll_sw/isoal.h" +#include "subsys/bluetooth/controller/ll_sw/ull_iso_types.h" + +#include "bs_types.h" +#include "bs_tracing.h" +#include "time_machine.h" +#include "bstests.h" + +#define FAIL(...) \ + do { \ + bst_result = Failed; \ + bs_trace_error_time_line(__VA_ARGS__); \ + } while (0) + +#define PASS(...) \ + do { \ + bst_result = Passed; \ + bs_trace_info_time(1, __VA_ARGS__); \ + } while (0) + +extern enum bst_result_t bst_result; + +static uint8_t mfg_data1[] = { 0xff, 0xff, 0x01, 0x02, 0x03, 0x04 }; +static uint8_t mfg_data2[] = { 0xff, 0xff, 0x05 }; + +static const struct bt_data per_ad_data1[] = { + BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data1, 6), +}; + +static const struct bt_data per_ad_data2[] = { + BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data2, 3), +}; + +static uint8_t chan_map[] = { 0x1F, 0XF1, 0x1F, 0xF1, 0x1F }; + +static bool volatile is_iso_connected; +static uint8_t volatile is_iso_disconnected; +static bool volatile deleting_pa_sync; + +#if !defined(CONFIG_TEST_LL_INTERFACE) +static void iso_connected(struct bt_iso_chan *chan); +static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason); +static void iso_recv(struct bt_iso_chan *chan, + const struct bt_iso_recv_info *info, struct net_buf *buf); + +static struct bt_iso_chan_ops iso_ops = { + .connected = iso_connected, + .disconnected = iso_disconnected, + .recv = iso_recv, +}; + +static struct bt_iso_chan_path iso_path_rx = { + .pid = BT_HCI_DATAPATH_ID_HCI +}; + +static struct bt_iso_chan_qos bis_iso_qos; +static struct bt_iso_chan_io_qos iso_tx_qos; +static struct bt_iso_chan_io_qos iso_rx_qos = { + .path = &iso_path_rx +}; + +static struct bt_iso_chan bis_iso_chan = { + .ops = &iso_ops, + .qos = &bis_iso_qos, +}; + +#define BIS_ISO_CHAN_COUNT 1 +static struct bt_iso_chan *bis_channels[BIS_ISO_CHAN_COUNT] = { &bis_iso_chan }; +static uint16_t seq_num; + +NET_BUF_POOL_FIXED_DEFINE(bis_tx_pool, BIS_ISO_CHAN_COUNT, + BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), + CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); + +#if defined(CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH) +static uint8_t test_rx_buffer[CONFIG_BT_CTLR_SYNC_ISO_PDU_LEN_MAX]; +static bool is_iso_vs_emitted; + +static isoal_status_t test_sink_sdu_alloc(const struct isoal_sink *sink_ctx, + const struct isoal_pdu_rx *valid_pdu, + struct isoal_sdu_buffer *sdu_buffer) +{ + sdu_buffer->dbuf = test_rx_buffer; + sdu_buffer->size = sizeof(test_rx_buffer); + + return ISOAL_STATUS_OK; +} + + +static isoal_status_t test_sink_sdu_emit(const struct isoal_sink *sink_ctx, + const struct isoal_emitted_sdu_frag *sdu_frag, + const struct isoal_emitted_sdu *sdu) +{ + printk("Vendor sink SDU fragment size %u / %u, seq_num %u, ts %u\n", + sdu_frag->sdu_frag_size, sdu->total_sdu_size, sdu_frag->sdu.sn, + sdu_frag->sdu.timestamp); + is_iso_vs_emitted = true; + + return ISOAL_STATUS_OK; +} + +static isoal_status_t test_sink_sdu_write(void *dbuf, + const size_t sdu_written, + const uint8_t *pdu_payload, + const size_t consume_len) +{ + memcpy((uint8_t *)dbuf + sdu_written, pdu_payload, consume_len); + + return ISOAL_STATUS_OK; +} + + +bool ll_data_path_sink_create(uint16_t handle, struct ll_iso_datapath *datapath, + isoal_sink_sdu_alloc_cb *sdu_alloc, + isoal_sink_sdu_emit_cb *sdu_emit, + isoal_sink_sdu_write_cb *sdu_write) +{ + ARG_UNUSED(handle); + ARG_UNUSED(datapath); + + *sdu_alloc = test_sink_sdu_alloc; + *sdu_emit = test_sink_sdu_emit; + *sdu_write = test_sink_sdu_write; + + printk("VS data path sink created\n"); + return true; +} +#endif /* CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH */ + +#define BUF_ALLOC_TIMEOUT_MS (30) /* milliseconds */ +NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT, + BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), + CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); + +static struct k_work_delayable iso_send_work; + +BUILD_ASSERT(sizeof(seq_num) <= CONFIG_BT_ISO_TX_MTU); + +static void iso_send(struct k_work *work) +{ + static uint8_t iso_data[CONFIG_BT_ISO_TX_MTU]; + static bool data_initialized; + struct net_buf *buf; + size_t iso_data_len; + int ret; + + if (!data_initialized) { + data_initialized = true; + + for (size_t i = 0; i < ARRAY_SIZE(iso_data); i++) { + iso_data[i] = (uint8_t)i; + } + } + + buf = net_buf_alloc(&tx_pool, K_MSEC(BUF_ALLOC_TIMEOUT_MS)); + if (!buf) { + FAIL("Data buffer allocate timeout on channel\n"); + return; + } + + net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE); + sys_put_le16(seq_num, iso_data); + iso_data_len = MAX(sizeof(seq_num), ((seq_num % CONFIG_BT_ISO_TX_MTU) + 1)); + net_buf_add_mem(buf, iso_data, iso_data_len); + + bs_trace_info_time(4, "ISO send: seq_num %u\n", seq_num); + ret = bt_iso_chan_send(&bis_iso_chan, buf, seq_num++); + if (ret < 0) { + FAIL("Unable to broadcast data on channel (%d)\n", ret); + net_buf_unref(buf); + return; + } + + k_work_schedule(&iso_send_work, K_USEC(9970)); +} +#endif /* !CONFIG_TEST_LL_INTERFACE */ + +static void setup_ext_adv(struct bt_le_ext_adv **adv) +{ + int err; + + printk("Create advertising set..."); + err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, NULL, adv); + if (err) { + FAIL("Failed to create advertising set (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Setting Periodic Advertising parameters..."); + err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_DEFAULT); + if (err) { + FAIL("Failed to set periodic advertising parameters (err %d)\n", + err); + return; + } + printk("success.\n"); + + printk("Enable Periodic Advertising..."); + err = bt_le_per_adv_start(*adv); + if (err) { + FAIL("Failed to enable periodic advertising (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Start extended advertising..."); + err = bt_le_ext_adv_start(*adv, BT_LE_EXT_ADV_START_DEFAULT); + if (err) { + printk("Failed to start extended advertising (err %d)\n", err); + return; + } + printk("success.\n"); +} + +static void teardown_ext_adv(struct bt_le_ext_adv *adv) +{ + int err; + + printk("Stop Periodic Advertising..."); + err = bt_le_per_adv_stop(adv); + if (err) { + FAIL("Failed to stop periodic advertising (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Stop Extended Advertising..."); + err = bt_le_ext_adv_stop(adv); + if (err) { + FAIL("Failed to stop extended advertising (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Deleting Extended Advertising..."); + err = bt_le_ext_adv_delete(adv); + if (err) { + FAIL("Failed to delete extended advertising (err %d)\n", err); + return; + } + printk("success.\n"); +} + +#if defined(CONFIG_TEST_LL_INTERFACE) +static void create_ll_big(uint8_t big_handle, struct bt_le_ext_adv *adv) +{ + uint16_t max_sdu = CONFIG_BT_CTLR_ADV_ISO_PDU_LEN_MAX; + uint8_t bcode[BT_ISO_BROADCAST_CODE_SIZE] = { 0 }; + uint32_t sdu_interval = 10000; /* us */ + uint16_t max_latency = 10; /* ms */ + uint8_t encryption = 0; + uint8_t bis_count = 1; /* TODO: Add support for multiple BIS per BIG */ + uint8_t phy = BIT(1); + uint8_t packing = 0; + uint8_t framing = 0; + uint8_t adv_handle; + uint8_t rtn = 0; + int err; + + printk("Creating LL BIG..."); + /* Assume that index == handle */ + adv_handle = bt_le_ext_adv_get_index(adv); + + err = ll_big_create(big_handle, adv_handle, bis_count, sdu_interval, + max_sdu, max_latency, rtn, phy, packing, framing, + encryption, bcode); + if (err) { + FAIL("Could not create BIG: %d\n", err); + return; + } + printk("success.\n"); +} + +static void terminate_ll_big(uint8_t big_handle) +{ + int err; + + printk("Terminating LL BIG..."); + err = ll_big_terminate(big_handle, BT_HCI_ERR_LOCALHOST_TERM_CONN); + if (err) { + FAIL("Could not terminate BIG: %d\n", err); + return; + } + printk("success.\n"); +} + +#else /* !CONFIG_TEST_LL_INTERFACE */ +static void create_big(struct bt_le_ext_adv *adv, struct bt_iso_big **big) +{ + struct bt_iso_big_create_param big_create_param = { 0 }; + int err; + + printk("Creating BIG...\n"); + big_create_param.bis_channels = bis_channels; + big_create_param.num_bis = BIS_ISO_CHAN_COUNT; + big_create_param.encryption = false; + big_create_param.interval = 10000; /* us */ + big_create_param.latency = 10; /* milliseconds */ + big_create_param.packing = 0; /* 0 - sequential; 1 - interleaved */ + big_create_param.framing = 0; /* 0 - unframed; 1 - framed */ + iso_tx_qos.sdu = CONFIG_BT_ISO_TX_MTU; /* bytes */ + iso_tx_qos.rtn = 2; + iso_tx_qos.phy = BT_GAP_LE_PHY_2M; + bis_iso_qos.tx = &iso_tx_qos; + bis_iso_qos.rx = NULL; + err = bt_iso_big_create(adv, &big_create_param, big); + if (err) { + FAIL("Could not create BIG: %d\n", err); + return; + } + printk("success.\n"); + + printk("Wait for ISO connected callback..."); + while (!is_iso_connected) { + k_sleep(K_MSEC(100)); + } + printk("ISO connected\n"); +} + +static void terminate_big(struct bt_iso_big *big) +{ + int err; + + printk("Terminating BIG...\n"); + err = bt_iso_big_terminate(big); + if (err) { + FAIL("Could not terminate BIG: %d\n", err); + return; + } + printk("success.\n"); + + printk("Wait for ISO disconnected callback..."); + while (is_iso_disconnected == 0U) { + k_sleep(K_MSEC(100)); + } + printk("ISO disconnected\n"); +} +#endif /* !CONFIG_TEST_LL_INTERFACE */ + +#if defined(CONFIG_BT_ISO_TEST_PARAMS) +static void create_advanced_big(struct bt_le_ext_adv *adv, struct bt_iso_big **big) +{ + struct bt_iso_big_create_param big_create_param; + int err; + + printk("Creating BIG...\n"); + big_create_param.bis_channels = bis_channels; + big_create_param.num_bis = BIS_ISO_CHAN_COUNT; + big_create_param.encryption = false; + big_create_param.interval = 10000; /* us */ + big_create_param.packing = 0; /* 0 - sequential; 1 - interleaved */ + big_create_param.framing = 0; /* 0 - unframed; 1 - framed */ + big_create_param.irc = BT_ISO_IRC_MIN; + big_create_param.pto = BT_ISO_PTO_MIN; + big_create_param.iso_interval = big_create_param.interval / 1250U; /* N * 10 ms */ + + iso_tx_qos.sdu = 502; /* bytes */ + iso_tx_qos.phy = BT_GAP_LE_PHY_2M; + iso_tx_qos.max_pdu = BT_ISO_PDU_MAX; + iso_tx_qos.burst_number = BT_ISO_BN_MIN; + + bis_iso_qos.tx = &iso_tx_qos; + bis_iso_qos.rx = NULL; + bis_iso_qos.num_subevents = BT_ISO_NSE_MIN; + + err = bt_iso_big_create(adv, &big_create_param, big); + if (err) { + FAIL("Could not create BIG: %d\n", err); + return; + } + printk("success.\n"); + + printk("Wait for ISO connected callback..."); + while (!is_iso_connected) { + k_sleep(K_MSEC(100)); + } + printk("ISO connected\n"); +} +#endif /* CONFIG_BT_ISO_TEST_PARAMS */ + +static void test_iso_main(void) +{ + struct bt_le_ext_adv *adv; + int err; + + printk("\n*ISO broadcast test*\n"); + + printk("Bluetooth initializing..."); + err = bt_enable(NULL); + if (err) { + FAIL("Could not init BT: %d\n", err); + return; + } + printk("success.\n"); + + setup_ext_adv(&adv); + +#if defined(CONFIG_TEST_LL_INTERFACE) + uint8_t big_handle = 0; + + create_ll_big(big_handle, adv); + +#else /* !CONFIG_TEST_LL_INTERFACE */ + struct bt_iso_big *big; + + create_big(adv, &big); + + k_work_init_delayable(&iso_send_work, iso_send); + k_work_schedule(&iso_send_work, K_NO_WAIT); +#endif /* !CONFIG_TEST_LL_INTERFACE */ + + k_sleep(K_MSEC(5000)); + + printk("Update periodic advertising data 1..."); + err = bt_le_per_adv_set_data(adv, per_ad_data1, + ARRAY_SIZE(per_ad_data1)); + if (err) { + FAIL("Failed to update periodic advertising data 1 (%d).\n", + err); + } + printk("success.\n"); + + k_sleep(K_MSEC(2500)); + + printk("Periodic Advertising and ISO Channel Map Update..."); + err = bt_le_set_chan_map(chan_map); + if (err) { + FAIL("Channel Map Update failed.\n"); + } + printk("success.\n"); + + k_sleep(K_MSEC(2500)); + + printk("Update periodic advertising data 2..."); + err = bt_le_per_adv_set_data(adv, per_ad_data2, + ARRAY_SIZE(per_ad_data2)); + if (err) { + FAIL("Failed to update periodic advertising data 2 (%d).\n", + err); + } + printk("success.\n"); + + k_sleep(K_MSEC(5000)); + +#if defined(CONFIG_TEST_LL_INTERFACE) + terminate_ll_big(big_handle); + +#else /* !CONFIG_TEST_LL_INTERFACE */ + k_work_cancel_delayable(&iso_send_work); + + terminate_big(big); + big = NULL; + +#if defined(CONFIG_BT_ISO_TEST_PARAMS) + /* Quick check to just verify that creating a BIG using advanced/test + * parameters work + */ + create_advanced_big(adv, &big); + + terminate_big(big); + big = NULL; +#endif /* CONFIG_BT_ISO_TEST_PARAMS */ +#endif /* !CONFIG_TEST_LL_INTERFACE */ + + k_sleep(K_MSEC(10000)); + + teardown_ext_adv(adv); + adv = NULL; + + PASS("ISO tests Passed\n"); +} + +static const char *phy2str(uint8_t phy) +{ + switch (phy) { + case 0: return "No packets"; + case BT_GAP_LE_PHY_1M: return "LE 1M"; + case BT_GAP_LE_PHY_2M: return "LE 2M"; + case BT_GAP_LE_PHY_CODED: return "LE Coded"; + default: return "Unknown"; + } +} + +#if !defined(CONFIG_TEST_LL_INTERFACE) +/** Print data as d_0 d_1 d_2 ... d_(n-2) d_(n-1) d_(n) to show the 3 first and 3 last octets + * + * Examples: + * 01 + * 0102 + * 010203 + * 01020304 + * 0102030405 + * 010203040506 + * 010203...050607 + * 010203...060708 + * etc. + */ +static void iso_print_data(uint8_t *data, size_t data_len) +{ + /* Maximum number of octets from each end of the data */ + const uint8_t max_octets = 3; + char data_str[35]; + size_t str_len; + + str_len = bin2hex(data, MIN(max_octets, data_len), data_str, sizeof(data_str)); + if (data_len > max_octets) { + if (data_len > (max_octets * 2)) { + static const char dots[] = "..."; + + strcat(&data_str[str_len], dots); + str_len += strlen(dots); + } + + str_len += bin2hex(data + (data_len - MIN(max_octets, data_len - max_octets)), + MIN(max_octets, data_len - max_octets), + data_str + str_len, + sizeof(data_str) - str_len); + } + + printk("\t %s\n", data_str); +} + +#define SEQ_NUM_MAX 1000U +static uint16_t expected_seq_num[CONFIG_BT_ISO_MAX_CHAN]; + +static void iso_recv(struct bt_iso_chan *chan, + const struct bt_iso_recv_info *info, struct net_buf *buf) +{ + uint16_t seq_num; + uint8_t index; + + index = bt_conn_index(chan->iso); + + printk("Incoming data channel %p (%u) flags 0x%x seq_num %u ts %u len %u:\n", + chan, index, info->flags, info->seq_num, info->ts, buf->len); + iso_print_data(buf->data, buf->len); + + seq_num = sys_get_le16(buf->data); + if (info->flags & BT_ISO_FLAGS_VALID) { + if (seq_num != expected_seq_num[index]) { + if (expected_seq_num[index]) { + FAIL("ISO data miss match, expected %u actual %u\n", + expected_seq_num[index], seq_num); + } + expected_seq_num[index] = seq_num; + } + + expected_seq_num[index]++; + + } else if (expected_seq_num[index] && + expected_seq_num[index] < SEQ_NUM_MAX) { + FAIL("%s: Invalid ISO data after valid ISO data reception.\n" + "Expected %u\n", __func__, expected_seq_num[index]); + } +} + +static void iso_connected(struct bt_iso_chan *chan) +{ + printk("ISO Channel %p connected\n", chan); + + seq_num = 0U; + is_iso_connected = true; +} + +static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason) +{ + printk("ISO Channel %p disconnected with reason 0x%02x\n", chan, reason); + + is_iso_disconnected = reason; +} +#endif /* !CONFIG_TEST_LL_INTERFACE */ + +static bool volatile is_sync; + +static void pa_sync_cb(struct bt_le_per_adv_sync *sync, + struct bt_le_per_adv_sync_synced_info *info) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); + + printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, " + "Interval 0x%04x (%u ms), PHY %s\n", + bt_le_per_adv_sync_get_index(sync), le_addr, + info->interval, info->interval * 5 / 4, phy2str(info->phy)); + + is_sync = true; +} + +static void pa_terminated_cb(struct bt_le_per_adv_sync *sync, + const struct bt_le_per_adv_sync_term_info *info) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); + + printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n", + bt_le_per_adv_sync_get_index(sync), le_addr); + + if (!deleting_pa_sync) { + FAIL("PA terminated unexpectedly\n"); + } else { + deleting_pa_sync = false; + } +} + +static bool volatile is_sync_recv; + +static void pa_recv_cb(struct bt_le_per_adv_sync *sync, + const struct bt_le_per_adv_sync_recv_info *info, + struct net_buf_simple *buf) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); + + printk("PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, " + "RSSI %i, CTE %u, data length %u\n", + bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power, + info->rssi, info->cte_type, buf->len); + + is_sync_recv = true; +} + +static void +pa_state_changed_cb(struct bt_le_per_adv_sync *sync, + const struct bt_le_per_adv_sync_state_info *info) +{ + printk("PER_ADV_SYNC[%u]: state changed, receive %s.\n", + bt_le_per_adv_sync_get_index(sync), + info->recv_enabled ? "enabled" : "disabled"); +} + +static bool volatile is_big_info; + +static void pa_biginfo_cb(struct bt_le_per_adv_sync *sync, + const struct bt_iso_biginfo *biginfo) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(biginfo->addr, le_addr, sizeof(le_addr)); + printk("BIG INFO[%u]: [DEVICE]: %s, sid 0x%02x, " + "num_bis %u, nse %u, interval 0x%04x (%u ms), " + "bn %u, pto %u, irc %u, max_pdu %u, " + "sdu_interval %u us, max_sdu %u, phy %s, " + "%s framing, %sencrypted\n", + bt_le_per_adv_sync_get_index(sync), le_addr, biginfo->sid, + biginfo->num_bis, biginfo->sub_evt_count, + biginfo->iso_interval, + (biginfo->iso_interval * 5 / 4), + biginfo->burst_number, biginfo->offset, + biginfo->rep_count, biginfo->max_pdu, biginfo->sdu_interval, + biginfo->max_sdu, phy2str(biginfo->phy), + biginfo->framing ? "with" : "without", + biginfo->encryption ? "" : "not "); + + if (!is_big_info) { + is_big_info = true; + } +} + +static struct bt_le_per_adv_sync_cb sync_cb = { + .synced = pa_sync_cb, + .term = pa_terminated_cb, + .recv = pa_recv_cb, + .state_changed = pa_state_changed_cb, + .biginfo = pa_biginfo_cb, +}; + +#define NAME_LEN 30 + +static bool data_cb(struct bt_data *data, void *user_data) +{ + char *name = user_data; + + switch (data->type) { + case BT_DATA_NAME_SHORTENED: + case BT_DATA_NAME_COMPLETE: + memcpy(name, data->data, MIN(data->data_len, NAME_LEN - 1)); + return false; + default: + return true; + } +} + +static bool volatile is_periodic; +static bt_addr_le_t per_addr; +static uint8_t per_sid; + +static void scan_recv(const struct bt_le_scan_recv_info *info, + struct net_buf_simple *buf) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + char name[NAME_LEN]; + + (void)memset(name, 0, sizeof(name)); + + bt_data_parse(buf, data_cb, name); + + bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); + printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s " + "C:%u S:%u D:%u SR:%u E:%u Prim: %s, Secn: %s, " + "Interval: 0x%04x (%u ms), SID: %u\n", + le_addr, info->adv_type, info->tx_power, info->rssi, name, + (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0, + (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0, + (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0, + (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0, + (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0, + phy2str(info->primary_phy), phy2str(info->secondary_phy), + info->interval, info->interval * 5 / 4, info->sid); + + if (info->interval) { + if (!is_periodic) { + is_periodic = true; + per_sid = info->sid; + bt_addr_le_copy(&per_addr, info->addr); + } + } +} + +static struct bt_le_scan_cb scan_callbacks = { + .recv = scan_recv, +}; + +static void test_iso_recv_main(void) +{ + struct bt_le_scan_param scan_param = { + .type = BT_LE_SCAN_TYPE_ACTIVE, + .options = BT_LE_SCAN_OPT_NONE, + .interval = 0x0004, + .window = 0x0004, + }; + struct bt_le_per_adv_sync_param sync_create_param; + struct bt_le_per_adv_sync *sync = NULL; + int err; + + printk("\n*ISO broadcast test*\n"); + + printk("Bluetooth initializing..."); + err = bt_enable(NULL); + if (err) { + FAIL("Could not init BT: %d\n", err); + return; + } + printk("success.\n"); + + printk("Scan callbacks register..."); + bt_le_scan_cb_register(&scan_callbacks); + printk("success.\n"); + + printk("Periodic Advertising callbacks register..."); + bt_le_per_adv_sync_cb_register(&sync_cb); + printk("Success.\n"); + + printk("Start scanning..."); + is_periodic = false; + err = bt_le_scan_start(&scan_param, NULL); + if (err) { + FAIL("Could not start scan: %d\n", err); + return; + } + printk("success.\n"); + + while (!is_periodic) { + k_sleep(K_MSEC(100)); + } + printk("Periodic Advertising found (SID: %u)\n", per_sid); + + printk("Creating Periodic Advertising Sync..."); + is_sync = false; + bt_addr_le_copy(&sync_create_param.addr, &per_addr); + sync_create_param.options = + BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED; + sync_create_param.sid = per_sid; + sync_create_param.skip = 0; + sync_create_param.timeout = 0xa; + err = bt_le_per_adv_sync_create(&sync_create_param, &sync); + if (err) { + FAIL("Could not create sync: %d\n", err); + return; + } + printk("success.\n"); + + /* TODO: Enable when advertiser is added */ + printk("Waiting for sync..."); + while (!is_sync) { + k_sleep(K_MSEC(100)); + } + + printk("Stop scanning..."); + err = bt_le_scan_stop(); + if (err) { + FAIL("Could not stop scan: %d\n", err); + return; + } + printk("success.\n"); + + printk("Wait for BIG Info Advertising Report..."); + is_big_info = false; + while (!is_big_info) { + k_sleep(K_MSEC(100)); + } + printk("success.\n"); + +#if defined(CONFIG_TEST_LL_INTERFACE) + uint8_t bcode[BT_ISO_BROADCAST_CODE_SIZE] = { 0 }; + uint16_t sync_timeout = 10; + uint8_t bis[1] = { 0x01, }; + uint8_t big_handle = 0; + uint8_t encryption = 0; + uint8_t mse = 0; + + printk("Creating BIG Sync..."); + err = ll_big_sync_create(big_handle, sync->handle, encryption, bcode, + mse, sync_timeout, ARRAY_SIZE(bis), bis); + if (err) { + FAIL("Could not create BIG sync: %d\n", err); + return; + } + printk("success.\n"); + + k_sleep(K_MSEC(5000)); + + printk("Terminating BIG Sync..."); + struct node_rx_pdu *node_rx = NULL; + + err = ll_big_sync_terminate(big_handle, (void **)&node_rx); + if (err) { + FAIL("Could not terminate BIG sync: %d\n", err); + return; + } + printk("success.\n"); + + if (node_rx) { + FAIL("Generated Node Rx for synchronized BIG.\n"); + } + + k_sleep(K_MSEC(5000)); + + printk("Creating BIG Sync after terminate..."); + err = ll_big_sync_create(big_handle, sync->handle, encryption, bcode, + mse, sync_timeout, ARRAY_SIZE(bis), bis); + if (err) { + FAIL("Could not create BIG sync: %d\n", err); + return; + } + printk("success.\n"); + + printk("Terminating BIG Sync..."); + node_rx = NULL; + err = ll_big_sync_terminate(big_handle, (void **)&node_rx); + if (err) { + FAIL("Could not terminate BIG sync: %d\n", err); + return; + } + printk("success.\n"); + + if (node_rx) { + node_rx->hdr.next = NULL; + ll_rx_mem_release((void **)&node_rx); + } + + printk("Deleting Periodic Advertising Sync..."); + deleting_pa_sync = true; + err = bt_le_per_adv_sync_delete(sync); + if (err) { + FAIL("Failed to delete periodic advertising sync (err %d)\n", + err); + return; + } + printk("success.\n"); + +#else /* !CONFIG_TEST_LL_INTERFACE */ + struct bt_iso_big_sync_param big_param = { 0, }; + struct bt_iso_big *big; + + printk("ISO BIG create sync..."); + is_iso_connected = false; + bis_iso_qos.tx = NULL; + bis_iso_qos.rx = &iso_rx_qos; + big_param.bis_channels = bis_channels; + big_param.num_bis = BIS_ISO_CHAN_COUNT; + big_param.bis_bitfield = BT_ISO_BIS_INDEX_BIT(1); /* BIS 1 selected */ + big_param.mse = 1; + big_param.sync_timeout = 100; /* 1000 ms */ + big_param.encryption = false; + iso_path_rx.pid = BT_HCI_DATAPATH_ID_HCI; + memset(big_param.bcode, 0, sizeof(big_param.bcode)); + err = bt_iso_big_sync(sync, &big_param, &big); + if (err) { + FAIL("Could not create BIG sync: %d\n", err); + return; + } + printk("success.\n"); + + printk("Wait for ISO connected callback..."); + while (!is_iso_connected) { + k_sleep(K_MSEC(100)); + } + + printk("ISO terminate BIG..."); + is_iso_disconnected = 0U; + err = bt_iso_big_terminate(big); + if (err) { + FAIL("Could not terminate BIG sync: %d\n", err); + return; + } + printk("success.\n"); + + printk("Waiting for ISO disconnected callback...\n"); + while (!is_iso_disconnected) { + k_sleep(K_MSEC(100)); + } + printk("disconnected.\n"); + + if (is_iso_disconnected != BT_HCI_ERR_LOCALHOST_TERM_CONN) { + FAIL("Local Host Terminate Failed.\n"); + } + + printk("ISO BIG create sync (test remote disconnect)..."); + is_iso_connected = false; + is_iso_disconnected = 0U; + memset(expected_seq_num, 0U, sizeof(expected_seq_num)); + err = bt_iso_big_sync(sync, &big_param, &big); + if (err) { + FAIL("Could not create BIG sync: %d\n", err); + return; + } + printk("success.\n"); + + printk("Wait for ISO connected callback..."); + while (!is_iso_connected) { + k_sleep(K_MSEC(100)); + } + printk("connected.\n"); + + printk("Waiting for ISO disconnected callback...\n"); + while (!is_iso_disconnected) { + k_sleep(K_MSEC(100)); + } + printk("disconnected.\n"); + + if (is_iso_disconnected != BT_HCI_ERR_REMOTE_USER_TERM_CONN) { + FAIL("Remote Host Terminate Failed.\n"); + } + + printk("Periodic sync receive enable...\n"); + err = bt_le_per_adv_sync_recv_enable(sync); + if (err) { + printk("failed (err %d)\n", err); + return; + } + printk("receive enabled.\n"); + + uint8_t check_countdown = 3; + + printk("Waiting for remote BIG terminate by checking for missing " + "%u BIG Info report...\n", check_countdown); + do { + is_sync_recv = false; + is_big_info = false; + while (!is_sync_recv) { + k_sleep(K_MSEC(100)); + } + + k_sleep(K_MSEC(100)); + + if (!is_big_info) { + if (!--check_countdown) { + break; + } + } + } while (1); + printk("success.\n"); + + printk("Deleting Periodic Advertising Sync..."); + deleting_pa_sync = true; + err = bt_le_per_adv_sync_delete(sync); + if (err) { + FAIL("Failed to delete periodic advertising sync (err %d)\n", + err); + return; + } + printk("success.\n"); + + for (int chan = 0; chan < CONFIG_BT_ISO_MAX_CHAN; chan++) { + if (expected_seq_num[chan] < SEQ_NUM_MAX) { + FAIL("ISO Data reception incomplete %u (%u).\n", + expected_seq_num[chan], SEQ_NUM_MAX); + return; + } + } +#endif /* !CONFIG_TEST_LL_INTERFACE */ + + PASS("ISO recv test Passed\n"); +} + +#if defined(CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH) +static void test_iso_recv_vs_dp_main(void) +{ + struct bt_le_scan_param scan_param = { + .type = BT_LE_SCAN_TYPE_ACTIVE, + .options = BT_LE_SCAN_OPT_NONE, + .interval = 0x0004, + .window = 0x0004, + }; + struct bt_le_per_adv_sync_param sync_create_param; + struct bt_le_per_adv_sync *sync = NULL; + int err; + + printk("Bluetooth initializing... "); + err = bt_enable(NULL); + if (err) { + FAIL("Could not init BT: %d\n", err); + return; + } + printk("success.\n"); + + printk("Scan callbacks register... "); + bt_le_scan_cb_register(&scan_callbacks); + printk("success.\n"); + + printk("Periodic Advertising callbacks register... "); + bt_le_per_adv_sync_cb_register(&sync_cb); + printk("success.\n"); + + printk("Configure vendor data path... "); + err = bt_configure_data_path(BT_HCI_DATAPATH_DIR_CTLR_TO_HOST, + BT_HCI_DATAPATH_ID_VS, 0U, NULL); + if (err) { + FAIL("Failed (err %d)\n", err); + return; + } + + printk("success.\n"); + printk("Start scanning... "); + is_periodic = false; + err = bt_le_scan_start(&scan_param, NULL); + if (err) { + FAIL("Could not start scan: %d\n", err); + return; + } + printk("success.\n"); + + while (!is_periodic) { + k_sleep(K_MSEC(100)); + } + printk("Periodic Advertising found (SID: %u)\n", per_sid); + + printk("Creating Periodic Advertising Sync... "); + is_sync = false; + + bt_addr_le_copy(&sync_create_param.addr, &per_addr); + sync_create_param.options = + BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED; + sync_create_param.sid = per_sid; + sync_create_param.skip = 0; + sync_create_param.timeout = 0xa; + + err = bt_le_per_adv_sync_create(&sync_create_param, &sync); + if (err) { + FAIL("Could not create sync: %d\n", err); + return; + } + printk("success.\n"); + + /* TODO: Enable when advertiser is added */ + printk("Waiting for sync...\n"); + while (!is_sync) { + k_sleep(K_MSEC(100)); + } + printk("success.\n"); + + printk("Stop scanning... "); + err = bt_le_scan_stop(); + if (err) { + FAIL("Could not stop scan: %d\n", err); + return; + } + printk("success.\n"); + + printk("Wait for BIG Info Advertising Report...\n"); + is_big_info = false; + while (!is_big_info) { + k_sleep(K_MSEC(100)); + } + printk("success.\n"); + + struct bt_iso_big_sync_param big_param = { 0, }; + struct bt_iso_big *big; + + printk("ISO BIG create sync... "); + is_iso_connected = false; + bis_iso_qos.tx = NULL; + bis_iso_qos.rx = &iso_rx_qos; + big_param.bis_channels = bis_channels; + big_param.num_bis = BIS_ISO_CHAN_COUNT; + big_param.bis_bitfield = BT_ISO_BIS_INDEX_BIT(1); /* BIS 1 selected */ + big_param.mse = 1; + big_param.sync_timeout = 100; /* 1000 ms */ + big_param.encryption = false; + memset(big_param.bcode, 0, sizeof(big_param.bcode)); + + is_iso_connected = false; + is_iso_disconnected = 0U; + is_iso_vs_emitted = false; + iso_path_rx.pid = BT_HCI_DATAPATH_ID_VS; + + err = bt_iso_big_sync(sync, &big_param, &big); + if (err) { + FAIL("Could not create BIG sync: %d\n", err); + return; + } + printk("success.\n"); + + printk("Wait for ISO connected callback... "); + while (!is_iso_connected) { + k_sleep(K_MSEC(100)); + } + + /* Allow some SDUs to be received */ + k_sleep(K_MSEC(100)); + + printk("ISO terminate BIG... "); + is_iso_disconnected = 0U; + err = bt_iso_big_terminate(big); + if (err) { + FAIL("Could not terminate BIG sync: %d\n", err); + return; + } + printk("success.\n"); + + printk("Waiting for ISO disconnected callback...\n"); + while (!is_iso_disconnected) { + k_sleep(K_MSEC(100)); + } + printk("disconnected.\n"); + + if (is_iso_disconnected != BT_HCI_ERR_LOCALHOST_TERM_CONN) { + FAIL("Local Host Terminate Failed.\n"); + } + + if (!is_iso_vs_emitted) { + FAIL("Emitting of VS SDUs failed.\n"); + } + + printk("success.\n"); + + printk("Deleting Periodic Advertising Sync... "); + deleting_pa_sync = true; + err = bt_le_per_adv_sync_delete(sync); + if (err) { + FAIL("Failed to delete periodic advertising sync (err %d)\n", + err); + return; + } + printk("success.\n"); + + PASS("ISO recv VS test Passed\n"); +} +#endif /* CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH */ + +static void test_iso_init(void) +{ + bst_ticker_set_next_tick_absolute(60e6); + bst_result = In_progress; +} + +static void test_iso_tick(bs_time_t HW_device_time) +{ + if (bst_result != Passed) { + FAIL("test failed (not passed after seconds)\n"); + } +} + +static const struct bst_test_instance test_def[] = { + { + .test_id = "broadcast", + .test_descr = "ISO broadcast", + .test_pre_init_f = test_iso_init, + .test_tick_f = test_iso_tick, + .test_main_f = test_iso_main + }, + { + .test_id = "receive", + .test_descr = "ISO receive", + .test_pre_init_f = test_iso_init, + .test_tick_f = test_iso_tick, + .test_main_f = test_iso_recv_main + }, +#if defined(CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH) + { + .test_id = "receive_vs_dp", + .test_descr = "ISO receive VS", + .test_pre_init_f = test_iso_init, + .test_tick_f = test_iso_tick, + .test_main_f = test_iso_recv_vs_dp_main + }, +#endif /* CONFIG_BT_CTLR_ISO_VENDOR_DATA_PATH */ + BSTEST_END_MARKER +}; + +struct bst_test_list *test_iso_install(struct bst_test_list *tests) +{ + return bst_add_tests(tests, test_def); +} diff --git a/tests/bsim/bluetooth/ll/bis/src/test_past.c b/tests/bsim/bluetooth/ll/bis/src/test_past.c new file mode 100644 index 00000000000000..9103bdceddc710 --- /dev/null +++ b/tests/bsim/bluetooth/ll/bis/src/test_past.c @@ -0,0 +1,788 @@ +/* + * Copyright (c) 2024 Demant A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "subsys/bluetooth/host/hci_core.h" +#include "subsys/bluetooth/controller/include/ll.h" +#include "subsys/bluetooth/controller/util/memq.h" +#include "subsys/bluetooth/controller/ll_sw/lll.h" + +#include "bs_types.h" +#include "bs_tracing.h" +#include "time_machine.h" +#include "bstests.h" + +static struct bt_conn *default_conn; + +extern enum bst_result_t bst_result; + +#define FAIL(...) \ + do { \ + bst_result = Failed; \ + bs_trace_error_time_line(__VA_ARGS__); \ + } while (0) + +#define PASS(...) \ + do { \ + bst_result = Passed; \ + bs_trace_info_time(1, __VA_ARGS__); \ + } while (0) + +extern enum bst_result_t bst_result; + +static K_SEM_DEFINE(sem_is_sync, 0, 1); +static K_SEM_DEFINE(sem_is_conn, 0, 1); + +/* Set timeout to 20s */ +#define K_SEM_TIMEOUT K_MSEC(20000) + +struct bt_le_per_adv_sync *default_sync; + +static void connected(struct bt_conn *conn, uint8_t conn_err) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + if (conn_err) { + FAIL("Failed to connect to %s (%u)\n", addr, conn_err); + return; + } + printk("Connected: %s\n", addr); + + k_sem_give(&sem_is_conn); +} + +static bool eir_found(struct bt_data *data, void *user_data) +{ + bt_addr_le_t *addr = user_data; + int i; + + printk("[AD]: %u data_len %u\n", data->type, data->data_len); + + switch (data->type) { + case BT_DATA_UUID16_SOME: + case BT_DATA_UUID16_ALL: + if (data->data_len % sizeof(uint16_t) != 0U) { + FAIL("AD malformed\n"); + return true; + } + + for (i = 0; i < data->data_len; i += sizeof(uint16_t)) { + const struct bt_uuid *uuid; + struct bt_le_conn_param *param; + uint16_t u16; + int err; + + memcpy(&u16, &data->data[i], sizeof(u16)); + uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(u16)); + if (bt_uuid_cmp(uuid, BT_UUID_HRS)) { + continue; + } + + err = bt_le_scan_stop(); + if (err) { + FAIL("Stop LE scan failed (err %d)\n", err); + continue; + } + + param = BT_LE_CONN_PARAM_DEFAULT; + err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, + param, &default_conn); + if (err) { + printk("Create conn failed (err %d)\n", err); + } + + return false; + } + } + + return true; +} + +static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, + struct net_buf_simple *ad) +{ + char dev[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(addr, dev, sizeof(dev)); + printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n", + dev, type, ad->len, rssi); + + /* We're only interested in connectable events */ + if (type == BT_GAP_ADV_TYPE_ADV_IND || + type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) { + bt_data_parse(ad, eir_found, (void *)addr); + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Disconnected: %s (reason 0x%02x)\n", addr, reason); + + if (default_conn != conn) { + return; + } + + bt_conn_unref(default_conn); + default_conn = NULL; +} + +static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, +}; + + +static void setup_ext_adv(struct bt_le_ext_adv **adv) +{ + int err; + + printk("Create advertising set..."); + err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, NULL, adv); + if (err) { + FAIL("Failed to create advertising set (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Setting Periodic Advertising parameters..."); + err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_DEFAULT); + if (err) { + FAIL("Failed to set periodic advertising parameters (err %d)\n", + err); + return; + } + printk("success.\n"); + + printk("Enable Periodic Advertising..."); + err = bt_le_per_adv_start(*adv); + if (err) { + FAIL("Failed to enable periodic advertising (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Start extended advertising..."); + err = bt_le_ext_adv_start(*adv, BT_LE_EXT_ADV_START_DEFAULT); + if (err) { + printk("Failed to start extended advertising (err %d)\n", err); + return; + } + printk("success.\n"); +} + +static void teardown_ext_adv(struct bt_le_ext_adv *adv) +{ + int err; + + printk("Stop Periodic Advertising..."); + err = bt_le_per_adv_stop(adv); + if (err) { + FAIL("Failed to stop periodic advertising (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Stop Extended Advertising..."); + err = bt_le_ext_adv_stop(adv); + if (err) { + FAIL("Failed to stop extended advertising (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Deleting Extended Advertising..."); + err = bt_le_ext_adv_delete(adv); + if (err) { + FAIL("Failed to delete extended advertising (err %d)\n", err); + return; + } + printk("success.\n"); +} + +static const char *phy2str(uint8_t phy) +{ + switch (phy) { + case 0: return "No packets"; + case BT_GAP_LE_PHY_1M: return "LE 1M"; + case BT_GAP_LE_PHY_2M: return "LE 2M"; + case BT_GAP_LE_PHY_CODED: return "LE Coded"; + default: return "Unknown"; + } +} + +static void pa_sync_cb(struct bt_le_per_adv_sync *sync, + struct bt_le_per_adv_sync_synced_info *info) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + + default_sync = sync; + + bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); + + printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, " + "Interval 0x%04x (%u ms), PHY %s\n", + bt_le_per_adv_sync_get_index(sync), le_addr, + info->interval, info->interval * 5 / 4, phy2str(info->phy)); + + k_sem_give(&sem_is_sync); + + printk("Stop scanning\n"); + bt_le_scan_stop(); + printk("success.\n"); +} + +static void pa_terminated_cb(struct bt_le_per_adv_sync *sync, + const struct bt_le_per_adv_sync_term_info *info) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); + + printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n", + bt_le_per_adv_sync_get_index(sync), le_addr); +} + +static void +pa_state_changed_cb(struct bt_le_per_adv_sync *sync, + const struct bt_le_per_adv_sync_state_info *info) +{ + printk("PER_ADV_SYNC[%u]: state changed, receive %s.\n", + bt_le_per_adv_sync_get_index(sync), + info->recv_enabled ? "enabled" : "disabled"); +} + + +static struct bt_le_per_adv_sync_cb sync_cb = { + .synced = pa_sync_cb, + .term = pa_terminated_cb, + .state_changed = pa_state_changed_cb, +}; + +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, + BT_UUID_16_ENCODE(BT_UUID_HRS_VAL), + BT_UUID_16_ENCODE(BT_UUID_BAS_VAL), + BT_UUID_16_ENCODE(BT_UUID_CTS_VAL)), +}; + +static void bt_ready(void) +{ + int err; + + printk("Peripheral Bluetooth initialized\n"); + + err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0); + if (err) { + FAIL("Advertising failed to start (err %d)\n", err); + return; + } + + printk("Advertising successfully started\n"); +} + +#define NAME_LEN 30 + +static bool data_cb(struct bt_data *data, void *user_data) +{ + char *name = user_data; + + switch (data->type) { + case BT_DATA_NAME_SHORTENED: + case BT_DATA_NAME_COMPLETE: + memcpy(name, data->data, MIN(data->data_len, NAME_LEN - 1)); + return false; + default: + return true; + } +} + +static bool volatile is_periodic; +static bt_addr_le_t per_addr; +static uint8_t per_sid; + +static void scan_recv(const struct bt_le_scan_recv_info *info, + struct net_buf_simple *buf) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + char name[NAME_LEN]; + + (void)memset(name, 0, sizeof(name)); + + bt_data_parse(buf, data_cb, name); + + bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); + printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s " + "C:%u S:%u D:%u SR:%u E:%u Prim: %s, Secn: %s, " + "Interval: 0x%04x (%u ms), SID: %u\n", + le_addr, info->adv_type, info->tx_power, info->rssi, name, + (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0, + (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0, + (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0, + (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0, + (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0, + phy2str(info->primary_phy), phy2str(info->secondary_phy), + info->interval, info->interval * 5 / 4, info->sid); + + if (info->interval) { + if (!is_periodic) { + is_periodic = true; + per_sid = info->sid; + bt_addr_le_copy(&per_addr, info->addr); + } + } +} + +static struct bt_le_scan_cb scan_callbacks = { + .recv = scan_recv, +}; + +static void test_broadcast_main(void) +{ + struct bt_le_ext_adv *adv; + int err; + + printk("\n*PA Broadcaster*\n"); + + printk("Bluetooth initializing..."); + err = bt_enable(NULL); + if (err) { + FAIL("Could not init BT: %d\n", err); + return; + } + printk("success.\n"); + + setup_ext_adv(&adv); + + k_sleep(K_MSEC(40000)); + + teardown_ext_adv(adv); + adv = NULL; + + PASS("Broadcast PA Passed\n"); +} + +static void test_broadcast_past_sender_main(void) +{ + struct bt_le_ext_adv *adv; + int err; + + printk("\n*Broadcaster*\n"); + + printk("Connection callbacks register...\n"); + bt_conn_cb_register(&conn_callbacks); + printk("Success.\n"); + + printk("Bluetooth initializing..."); + err = bt_enable(NULL); + if (err) { + FAIL("Could not init BT: %d\n", err); + return; + } + printk("success.\n"); + + printk("Scanning for peripheral\n"); + err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, device_found); + if (err) { + FAIL("Scanning failed to start (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Waiting for connection...\n"); + err = k_sem_take(&sem_is_conn, K_SEM_TIMEOUT); + if (err) { + FAIL("Failed to connect (err %d)\n", err); + return; + } + printk("success.\n"); + + setup_ext_adv(&adv); + + k_sleep(K_MSEC(500)); + + printk("Connection established and broadcasting - sending PAST\n"); + err = bt_le_per_adv_set_info_transfer(adv, default_conn, 0); + if (err != 0) { + FAIL("Could not transfer periodic adv sync: %d", err); + return; + } + printk("success.\n"); + + printk("Wait 20s for PAST to be send\n"); + k_sleep(K_SEM_TIMEOUT); + + printk("Disconnect before actually passing\n"); + err = bt_conn_disconnect(default_conn, + BT_HCI_ERR_REMOTE_USER_TERM_CONN); + if (err) { + FAIL("Disconnection failed (err %d)\n", err); + return; + } + printk("success.\n"); + + teardown_ext_adv(adv); + adv = NULL; + + PASS("Broadcast PA Passed\n"); +} + +static void test_past_send_main(void) +{ + struct bt_le_scan_param scan_param = { + .type = BT_LE_SCAN_TYPE_ACTIVE, + .options = BT_LE_SCAN_OPT_NONE, + .interval = 0x0004, + .window = 0x0004, + }; + struct bt_le_per_adv_sync_param sync_create_param; + struct bt_le_per_adv_sync *sync = NULL; + uint16_t service_data = 0; + int err; + + printk("\n*Send PAST test*\n"); + + printk("Connection callbacks register...\n"); + bt_conn_cb_register(&conn_callbacks); + printk("Success.\n"); + + printk("Bluetooth initializing...\n"); + err = bt_enable(NULL); + if (err) { + FAIL("Could not init BT: %d\n", err); + return; + } + printk("success.\n"); + + printk("Scan callbacks register...\n"); + bt_le_scan_cb_register(&scan_callbacks); + printk("success.\n"); + + printk("Periodic Advertising callbacks register...\n"); + bt_le_per_adv_sync_cb_register(&sync_cb); + printk("Success.\n"); + + printk("Start scanning...\n"); + is_periodic = false; + err = bt_le_scan_start(&scan_param, NULL); + if (err) { + FAIL("Could not start scan: %d\n", err); + return; + } + printk("success.\n"); + + while (!is_periodic) { + k_sleep(K_MSEC(100)); + } + printk("Periodic Advertising found (SID: %u)\n", per_sid); + + printk("Creating Periodic Advertising Sync...\n"); + bt_addr_le_copy(&sync_create_param.addr, &per_addr); + sync_create_param.options = + BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED; + sync_create_param.sid = per_sid; + sync_create_param.skip = 0; + sync_create_param.timeout = 0xa; + + err = bt_le_per_adv_sync_create(&sync_create_param, &sync); + if (err) { + FAIL("Could not create sync: %d\n", err); + return; + } + printk("success.\n"); + + printk("Waiting for sync...\n"); + err = k_sem_take(&sem_is_sync, K_SEM_TIMEOUT); + if (err) { + FAIL("failed (err %d)\n", err); + return; + } + printk("success.\n"); + + + printk("Scanning for peripheral\n"); + err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, device_found); + if (err) { + FAIL("Scanning failed to start (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Waiting for connection...\n"); + err = k_sem_take(&sem_is_conn, K_SEM_TIMEOUT); + if (err) { + FAIL("Failed to connect (err %d)\n", err); + return; + } + printk("success.\n"); + + k_sleep(K_MSEC(1000)); + + printk("Connection established - sending PAST\n"); + err = bt_le_per_adv_sync_transfer(sync, default_conn, service_data); + if (err != 0) { + FAIL("Could not transfer periodic adv sync: %d", err); + return; + } + printk("success.\n"); + + printk("Wait 20s for PAST to be send\n"); + k_sleep(K_SEM_TIMEOUT); + + printk("Disconnect before actually passing\n"); + err = bt_conn_disconnect(default_conn, + BT_HCI_ERR_REMOTE_USER_TERM_CONN); + if (err) { + FAIL("Disconnection failed (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Deleting Periodic Advertising Sync...\n"); + err = bt_le_per_adv_sync_delete(sync); + if (err) { + FAIL("Failed to delete periodic advertising sync (err %d)\n", + err); + return; + } + printk("success.\n"); + + PASS("PAST send test Passed\n"); +} + +static void test_past_recv_main(void) +{ + struct bt_le_per_adv_sync_transfer_param past_param; + int err; + + printk("\n*Receive PAST Test*\n"); + + printk("Connection callbacks register...\n"); + bt_conn_cb_register(&conn_callbacks); + printk("Success.\n"); + + printk("Bluetooth initializing...\n"); + err = bt_enable(NULL); + if (err) { + FAIL("Could not init BT: %d\n", err); + return; + } + printk("success.\n"); + + printk("Scan callbacks register...\n"); + bt_le_scan_cb_register(&scan_callbacks); + printk("success.\n"); + + printk("Periodic Advertising callbacks register...\n"); + bt_le_per_adv_sync_cb_register(&sync_cb); + printk("Success.\n"); + + printk("Set default PAST Params.\n"); + past_param.skip = 1; + past_param.timeout = 1000; /* 10 seconds */ + past_param.options = BT_LE_PER_ADV_SYNC_TRANSFER_OPT_FILTER_DUPLICATES; + + err = bt_le_per_adv_sync_transfer_subscribe(NULL, &past_param); + if (err) { + FAIL("Failed to set default past parameters(err %d)\n", + err); + return; + } + printk("success.\n"); + + bt_ready(); + + printk("Waiting for connection...\n"); + err = k_sem_take(&sem_is_conn, K_SEM_TIMEOUT); + if (err) { + FAIL("Failed to connect (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Set PAST parameters for connection...\n"); + err = bt_le_per_adv_sync_transfer_subscribe(default_conn, &past_param); + if (err) { + FAIL("Failed to set default past parameters(err %d)\n", + err); + return; + } + printk("success.\n"); + + printk("Wait 20s for Periodic advertisement sync to be established\n"); + err = k_sem_take(&sem_is_sync, K_SEM_TIMEOUT); + if (err) { + FAIL("failed (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Deleting Periodic Advertising Sync..."); + err = bt_le_per_adv_sync_delete(default_sync); + if (err) { + FAIL("Failed to delete periodic advertising sync (err %d)\n", + err); + return; + } + printk("success.\n"); + + PASS("PAST recv test Passed\n"); +} + +static void test_past_recv_main_default_param(void) +{ + struct bt_le_per_adv_sync_transfer_param past_param; + int err; + + printk("\n*Receive PAST Test*\n"); + + printk("Connection callbacks register...\n"); + bt_conn_cb_register(&conn_callbacks); + printk("Success.\n"); + + printk("Bluetooth initializing...\n"); + err = bt_enable(NULL); + if (err) { + FAIL("Could not init BT: %d\n", err); + return; + } + printk("success.\n"); + + printk("Scan callbacks register...\n"); + bt_le_scan_cb_register(&scan_callbacks); + printk("success.\n"); + + printk("Periodic Advertising callbacks register...\n"); + bt_le_per_adv_sync_cb_register(&sync_cb); + printk("Success.\n"); + + printk("Set default PAST Params.\n"); + past_param.skip = 1; + past_param.timeout = 1000; /* 10 seconds */ + past_param.options = BT_LE_PER_ADV_SYNC_TRANSFER_OPT_FILTER_DUPLICATES; + + err = bt_le_per_adv_sync_transfer_subscribe(NULL, &past_param); + if (err) { + FAIL("Failed to set default past parameters(err %d)\n", + err); + return; + } + printk("success.\n"); + + bt_ready(); + + printk("Waiting for connection...\n"); + err = k_sem_take(&sem_is_conn, K_SEM_TIMEOUT); + if (err) { + FAIL("Failed to connect (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Wait 20s for Periodic advertisement sync to be established\n"); + err = k_sem_take(&sem_is_sync, K_SEM_TIMEOUT); + if (err) { + FAIL("failed (err %d)\n", err); + return; + } + printk("success.\n"); + + printk("Deleting Periodic Advertising Sync..."); + err = bt_le_per_adv_sync_delete(default_sync); + if (err) { + FAIL("Failed to delete periodic advertising sync (err %d)\n", + err); + return; + } + printk("success.\n"); + + PASS("PAST recv test Passed\n"); +} + +static void test_past_init(void) +{ + bst_ticker_set_next_tick_absolute(60e6); + bst_result = In_progress; +} + +static void test_past_tick(bs_time_t HW_device_time) +{ + if (bst_result != Passed) { + FAIL("test failed (not passed after seconds)\n"); + } +} + +static const struct bst_test_instance test_def[] = { + { + .test_id = "broadcast_pa", + .test_descr = "Periodic Advertisement broadcaster", + .test_pre_init_f = test_past_init, + .test_tick_f = test_past_tick, + .test_main_f = test_broadcast_main + }, + { + .test_id = "receive_past", + .test_descr = "Peripheral device, waiting for connection " + "and then waits for receiving PAST, then syncs to PA", + .test_pre_init_f = test_past_init, + .test_tick_f = test_past_tick, + .test_main_f = test_past_recv_main + }, + { + .test_id = "receive_past_default_param", + .test_descr = "Peripheral device, waiting for connection " + "and then waits for receiving PAST with the default PAST parameter set," + " then syncs to PA", + .test_pre_init_f = test_past_init, + .test_tick_f = test_past_tick, + .test_main_f = test_past_recv_main_default_param + }, + { + .test_id = "send_past", + .test_descr = "Central that syncs to PA from broadcaster," + "connects to peripheral and sends PAST", + .test_pre_init_f = test_past_init, + .test_tick_f = test_past_tick, + .test_main_f = test_past_send_main + }, + { + .test_id = "broadcast_past_sender", + .test_descr = "PA broadcaster, connects and sends PAST to peripheral", + .test_pre_init_f = test_past_init, + .test_tick_f = test_past_tick, + .test_main_f = test_broadcast_past_sender_main + }, + BSTEST_END_MARKER +}; + +struct bst_test_list *test_past_install(struct bst_test_list *tests) +{ + return bst_add_tests(tests, test_def); +} diff --git a/tests/bsim/bluetooth/ll/bis/testcase.yaml b/tests/bsim/bluetooth/ll/bis/testcase.yaml index c13d68f3fb790d..2b75f6b2c28bc8 100644 --- a/tests/bsim/bluetooth/ll/bis/testcase.yaml +++ b/tests/bsim/bluetooth/ll/bis/testcase.yaml @@ -40,3 +40,14 @@ tests: harness: bsim harness_config: bsim_exe_name: tests_bsim_bluetooth_ll_bis_prj_vs_dp_conf + bluetooth.ll.bis_past: + extra_args: CONF_FILE=prj_past.conf + platform_allow: + - nrf52_bsim + - nrf5340bsim/nrf5340/cpunet + integration_platforms: + - nrf52_bsim + - nrf5340bsim/nrf5340/cpunet + harness: bsim + harness_config: + bsim_exe_name: tests_bsim_bluetooth_ll_bis_prj_past_conf diff --git a/tests/bsim/bluetooth/ll/bis/tests_scripts/past.sh b/tests/bsim/bluetooth/ll/bis/tests_scripts/past.sh new file mode 100755 index 00000000000000..3a94f0668e9a6d --- /dev/null +++ b/tests/bsim/bluetooth/ll/bis/tests_scripts/past.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Copyright (c) 2024 Demant A/S +# SPDX-License-Identifier: Apache-2.0 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +# Basic PAST Test: Broadcaster is broadcasting Periodic Advertisements (PA). +# A central device syncronizes to the PA after it is synced, +# it will connect to the peripheral device. +# After connection is established the Central send a Periodic Sync Transfer (PAST) +# to the peripheral, which then synchronizes to the PA. +simulation_id="past_basic" +verbosity_level=2 + +cd ${BSIM_OUT_PATH}/bin + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_ll_bis_prj_past_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=receive_past + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_ll_bis_prj_past_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=broadcast_pa + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_ll_bis_prj_past_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=2 -testid=send_past + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=3 -sim_length=60e6 $@ + +wait_for_background_jobs diff --git a/tests/bsim/bluetooth/ll/bis/tests_scripts/past_default_past_params.sh b/tests/bsim/bluetooth/ll/bis/tests_scripts/past_default_past_params.sh new file mode 100755 index 00000000000000..1f425572e0898c --- /dev/null +++ b/tests/bsim/bluetooth/ll/bis/tests_scripts/past_default_past_params.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Copyright (c) 2024 Demant A/S +# SPDX-License-Identifier: Apache-2.0 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +# Basic PAST Test: Broadcaster is broadcasting Periodic Advertisements (PA). +# A central device syncronizes to the PA after it is synced, +# it will connect to the peripheral device. +# The Peripheral will subcribe to PAST using the default PAST Params +# After connection is established the Central send a Periodic Sync Transfer (PAST) +# to the peripheral, which then synchronizes to the PA. +simulation_id="past_basic_default_params" +verbosity_level=2 + +cd ${BSIM_OUT_PATH}/bin + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_ll_bis_prj_past_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=receive_past_default_param + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_ll_bis_prj_past_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=broadcast_pa + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_ll_bis_prj_past_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=2 -testid=send_past + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=3 -sim_length=60e6 $@ + +wait_for_background_jobs diff --git a/tests/bsim/bluetooth/ll/bis/tests_scripts/past_send_from_broadcaster.sh b/tests/bsim/bluetooth/ll/bis/tests_scripts/past_send_from_broadcaster.sh new file mode 100755 index 00000000000000..8bf001d2be14e0 --- /dev/null +++ b/tests/bsim/bluetooth/ll/bis/tests_scripts/past_send_from_broadcaster.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Copyright (c) 2024 Demant A/S +# SPDX-License-Identifier: Apache-2.0 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +# Basic PAST Test: Broadcaster connects to peripheral, after connection is established +# and it is Periodic Advertising (PA), the broadcaster send a Periodic Sync Transfer (PAST) +# to the peripheral, which then synchronizes to the PA. +simulation_id="past_send_from_broadcaster" +verbosity_level=2 + +cd ${BSIM_OUT_PATH}/bin + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_ll_bis_prj_past_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=receive_past + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_ll_bis_prj_past_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=broadcast_past_sender + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=2 -sim_length=60e6 $@ + +wait_for_background_jobs diff --git a/tests/bsim/bluetooth/ll/compile.sh b/tests/bsim/bluetooth/ll/compile.sh index c27c55a0047742..bdb385c65108ae 100755 --- a/tests/bsim/bluetooth/ll/compile.sh +++ b/tests/bsim/bluetooth/ll/compile.sh @@ -25,6 +25,7 @@ app=tests/bsim/bluetooth/ll/bis compile app=tests/bsim/bluetooth/ll/bis conf_overlay=overlay-ll_interface.conf compile app=tests/bsim/bluetooth/ll/bis conf_overlay=overlay-ticker_expire_info.conf compile app=tests/bsim/bluetooth/ll/bis conf_file=prj_vs_dp.conf compile +app=tests/bsim/bluetooth/ll/bis conf_file=prj_past.conf compile app=tests/bsim/bluetooth/ll/edtt/hci_test_app \ conf_file=prj_dut_llcp.conf compile From 89eaa5ca0e153676e80e66a697bf823332679f16 Mon Sep 17 00:00:00 2001 From: Lucas Mathias Balling Date: Fri, 4 Oct 2024 13:34:42 +0200 Subject: [PATCH 5/5] Bluetooth: Audio: Fix PAST support for bap_scan_delegator Fixed PAST support for bap_scan_delegator Signed-off-by: Lucas Mathias Balling --- subsys/bluetooth/audio/bap_scan_delegator.c | 7 +++++++ .../audio/src/bap_scan_delegator_test.c | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/subsys/bluetooth/audio/bap_scan_delegator.c b/subsys/bluetooth/audio/bap_scan_delegator.c index 39ecf17062edc6..a68f6b4316327d 100644 --- a/subsys/bluetooth/audio/bap_scan_delegator.c +++ b/subsys/bluetooth/audio/bap_scan_delegator.c @@ -822,6 +822,7 @@ static int scan_delegator_mod_src(struct bt_conn *conn, */ if (pa_sync != BT_BAP_BASS_PA_REQ_NO_SYNC && state->pa_sync_state != BT_BAP_PA_STATE_SYNCED) { + const uint8_t pa_sync_state = state->pa_sync_state; const int err = pa_sync_request(conn, state, pa_sync, pa_interval); @@ -834,6 +835,12 @@ static int scan_delegator_mod_src(struct bt_conn *conn, err); return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED); + } else if (pa_sync_state != state->pa_sync_state) { + /* Temporary work around if the state is changed when pa_sync_request is + * called. See https://github.com/zephyrproject-rtos/zephyr/issues/79308 for + * more information about this issue. + */ + state_changed = true; } } else if (pa_sync == BT_BAP_BASS_PA_REQ_NO_SYNC && (state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ || diff --git a/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c b/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c index 6d34ee21a4a1ac..10a41acb6546e7 100644 --- a/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c @@ -138,6 +138,7 @@ static int pa_sync_past(struct bt_conn *conn, struct bt_le_per_adv_sync_transfer_param param = { 0 }; int err; + param.options = BT_LE_PER_ADV_SYNC_TRANSFER_OPT_FILTER_DUPLICATES; param.skip = PA_SYNC_SKIP; param.timeout = interval_to_sync_timeout(pa_interval); @@ -267,6 +268,13 @@ static int pa_sync_req_cb(struct bt_conn *conn, if (past_avail) { err = pa_sync_past(conn, state, pa_interval); + if (err == 0) { + err = bt_bap_scan_delegator_set_pa_state(state->recv_state->src_id, + BT_BAP_PA_STATE_INFO_REQ); + if (err != 0) { + printk("Failed to set INFO_REQ state: %d", err); + } + } } else { err = pa_sync_no_past(state, pa_interval); } @@ -358,6 +366,14 @@ static void pa_synced_cb(struct bt_le_per_adv_sync *sync, printk("PA %p synced\n", sync); + if (info->conn) { /* if from PAST */ + for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { + if (!sync_states[i].pa_sync) { + sync_states[i].pa_sync = sync; + } + } + } + state = sync_state_get_by_pa(sync); if (state == NULL) { FAIL("Could not get sync state from PA sync %p\n", sync);