From c42a2fd4bcc7532fcdf7a3f15a1e80c611a372d1 Mon Sep 17 00:00:00 2001 From: Magdalena Kasenberg Date: Mon, 10 Feb 2025 14:14:14 +0100 Subject: [PATCH 1/6] nimble/ll: Refactor transitions New Bluetooth features require fast RX-to-RX and TX-to-TX transitions and use a different TIFS than 150us. Co-authored-by: Andrzej Kaczmarek --- .../controller/include/controller/ble_phy.h | 21 +- nimble/controller/src/ble_ll_adv.c | 35 +- nimble/controller/src/ble_ll_conn.c | 11 +- nimble/controller/src/ble_ll_dtm.c | 3 +- nimble/controller/src/ble_ll_iso_big.c | 17 +- nimble/controller/src/ble_ll_scan.c | 3 +- nimble/controller/src/ble_ll_scan_aux.c | 4 +- nimble/drivers/dialog_cmac/src/ble_phy.c | 16 +- nimble/drivers/native/src/ble_phy.c | 11 +- nimble/drivers/nrf51/src/ble_phy.c | 15 +- nimble/drivers/nrf5x/src/ble_phy.c | 493 ++++++++++-------- 11 files changed, 352 insertions(+), 277 deletions(-) diff --git a/nimble/controller/include/controller/ble_phy.h b/nimble/controller/include/controller/ble_phy.h index fe70044234..ad7a6974d3 100644 --- a/nimble/controller/include/controller/ble_phy.h +++ b/nimble/controller/include/controller/ble_phy.h @@ -62,10 +62,11 @@ struct os_mbuf; #define BLE_PHY_STATE_TX (2) /* BLE PHY transitions */ -#define BLE_PHY_TRANSITION_NONE (0) -#define BLE_PHY_TRANSITION_RX_TX (1) -#define BLE_PHY_TRANSITION_TX_RX (2) -#define BLE_PHY_TRANSITION_TX_TX (3) +#define BLE_PHY_TRANSITION_NONE (0) +#define BLE_PHY_TRANSITION_TO_TX (1) +#define BLE_PHY_TRANSITION_TO_RX (2) +#define BLE_PHY_TRANSITION_TO_TX_ISO_SUBEVENT (3) +#define BLE_PHY_TRANSITION_TO_RX_ISO_SUBEVENT (4) /* PHY error codes */ #define BLE_PHY_ERR_RADIO_STATE (1) @@ -88,14 +89,8 @@ int ble_phy_init(void); int ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit); uint8_t ble_phy_chan_get(void); -#if MYNEWT_VAL(BLE_PHY_VARIABLE_TIFS) -/* Set T_ifs time for next transition */ -void ble_phy_tifs_set(uint16_t tifs); -#endif - -/* Set T_ifs for tx-tx transitions. Anchor is 0 for start of previous PDU, - * non-zero for end of PDU */ -void ble_phy_tifs_txtx_set(uint16_t usecs, uint8_t anchor); +/* Set direction of the next transition */ +void ble_phy_transition_set(uint8_t trans, uint16_t usecs); /* Set transmit start time */ int ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs); @@ -110,7 +105,7 @@ typedef uint8_t (*ble_phy_tx_pducb_t)(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte); /* Place the PHY into transmit mode */ -int ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans); +int ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg); /* Copies the received PHY buffer into the allocated pdu */ void ble_phy_rxpdu_copy(uint8_t *dptr, struct os_mbuf *rxpdu); diff --git a/nimble/controller/src/ble_ll_adv.c b/nimble/controller/src/ble_ll_adv.c index e62b2c3a66..b82cc28737 100644 --- a/nimble/controller/src/ble_ll_adv.c +++ b/nimble/controller/src/ble_ll_adv.c @@ -1126,7 +1126,6 @@ static int ble_ll_adv_tx_start_cb(struct ble_ll_sched_item *sch) { int rc; - uint8_t end_trans; uint32_t txstart; struct ble_ll_adv_sm *advsm; #if MYNEWT_VAL(BLE_LL_PHY) && MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) @@ -1196,18 +1195,18 @@ ble_ll_adv_tx_start_cb(struct ble_ll_sched_item *sch) if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) && ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) || (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE))) { - end_trans = BLE_PHY_TRANSITION_TX_RX; + ble_phy_transition_set(BLE_PHY_TRANSITION_TO_RX, BLE_LL_IFS); ble_phy_set_txend_cb(NULL, NULL); } else { - end_trans = BLE_PHY_TRANSITION_NONE; + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); } /* Transmit advertisement */ #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) - rc = ble_phy_tx(ble_ll_adv_pdu_make, advsm, end_trans); + rc = ble_phy_tx(ble_ll_adv_pdu_make, advsm); #else - rc = ble_phy_tx(ble_ll_adv_legacy_pdu_make, advsm, end_trans); + rc = ble_phy_tx(ble_ll_adv_legacy_pdu_make, advsm); #endif if (rc) { goto adv_tx_done; @@ -1281,7 +1280,6 @@ static int ble_ll_adv_secondary_tx_start_cb(struct ble_ll_sched_item *sch) { int rc; - uint8_t end_trans; uint32_t txstart; struct ble_ll_adv_sm *advsm; ble_phy_tx_pducb_t pducb; @@ -1340,22 +1338,22 @@ ble_ll_adv_secondary_tx_start_cb(struct ble_ll_sched_item *sch) /* Set phy mode based on type of advertisement */ if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { - end_trans = BLE_PHY_TRANSITION_TX_RX; + ble_phy_transition_set(BLE_PHY_TRANSITION_TO_RX, BLE_LL_IFS); ble_phy_set_txend_cb(NULL, NULL); pducb = ble_ll_adv_aux_pdu_make; } else if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) && advsm->aux_first_pdu) { - end_trans = BLE_PHY_TRANSITION_TX_RX; + ble_phy_transition_set(BLE_PHY_TRANSITION_TO_RX, BLE_LL_IFS); ble_phy_set_txend_cb(NULL, NULL); pducb = ble_ll_adv_aux_scannable_pdu_make; } else { - end_trans = BLE_PHY_TRANSITION_NONE; + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); pducb = ble_ll_adv_aux_pdu_make; } /* Transmit advertisement */ - rc = ble_phy_tx(pducb, advsm, end_trans); + rc = ble_phy_tx(pducb, advsm); if (rc) { goto adv_aux_dropped; } @@ -2343,8 +2341,9 @@ ble_ll_adv_sync_tx_start_cb(struct ble_ll_sched_item *sch) #endif /* Transmit advertisement */ + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); ble_phy_set_txend_cb(ble_ll_adv_sync_tx_end, advsm); - rc = ble_phy_tx(ble_ll_adv_sync_pdu_make, advsm, BLE_PHY_TRANSITION_NONE); + rc = ble_phy_tx(ble_ll_adv_sync_pdu_make, advsm); if (rc) { goto adv_tx_done; } @@ -4568,6 +4567,8 @@ ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu) */ ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); + #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) if (advsm->flags & BLE_LL_ADV_SM_FLAG_SCAN_REQ_NOTIF) { ble_ll_hci_ev_send_scan_req_recv(advsm->adv_instance, peer, @@ -4581,12 +4582,10 @@ ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu) */ advsm->rx_ble_hdr = ble_hdr; - rc = ble_phy_tx(ble_ll_adv_scan_rsp_pdu_make, advsm, - BLE_PHY_TRANSITION_NONE); + rc = ble_phy_tx(ble_ll_adv_scan_rsp_pdu_make, advsm); advsm->rx_ble_hdr = NULL; #else - rc = ble_phy_tx(ble_ll_adv_scan_rsp_legacy_pdu_make, advsm, - BLE_PHY_TRANSITION_NONE); + rc = ble_phy_tx(ble_ll_adv_scan_rsp_legacy_pdu_make, advsm); #endif if (!rc) { @@ -4620,9 +4619,9 @@ ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu) rsp_data.peer = rxbuf + BLE_LL_PDU_HDR_LEN; rsp_data.rxadd = rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK; + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); - rc = ble_phy_tx(ble_ll_adv_aux_conn_rsp_pdu_make, &rsp_data, - BLE_PHY_TRANSITION_NONE); + rc = ble_phy_tx(ble_ll_adv_aux_conn_rsp_pdu_make, &rsp_data); if (!rc) { ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD); STATS_INC(ble_ll_stats, aux_conn_rsp_tx); @@ -4913,6 +4912,8 @@ ble_ll_adv_rx_isr_start(uint8_t pdu_type) */ if (rc < 0) { ble_ll_adv_tx_done(advsm); + } else if (rc > 0) { + ble_phy_transition_set(BLE_PHY_TRANSITION_TO_TX, BLE_LL_IFS); } return rc; diff --git a/nimble/controller/src/ble_ll_conn.c b/nimble/controller/src/ble_ll_conn.c index bd3f2f64d3..e08376a587 100644 --- a/nimble/controller/src/ble_ll_conn.c +++ b/nimble/controller/src/ble_ll_conn.c @@ -1348,7 +1348,7 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm) txend_func = ble_ll_conn_wait_txend; } else { /* Wait for a response here */ - end_transition = BLE_PHY_TRANSITION_TX_RX; + end_transition = BLE_PHY_TRANSITION_TO_RX; txend_func = NULL; } @@ -1436,9 +1436,11 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm) } #endif + ble_phy_transition_set(end_transition, BLE_LL_IFS); + /* Set transmit end callback */ ble_phy_set_txend_cb(txend_func, connsm); - rc = ble_phy_tx(ble_ll_tx_mbuf_pducb, m, end_transition); + rc = ble_phy_tx(ble_ll_tx_mbuf_pducb, m); if (!rc) { /* Log transmit on connection state */ cur_txlen = ble_hdr->txinfo.pyld_len; @@ -3275,9 +3277,10 @@ ble_ll_conn_send_connect_req(struct os_mbuf *rxpdu, ble_ll_conn_prepare_connect_ind(connsm, ble_ll_scan_get_pdu_data(), addrd, rxhdr->rxinfo.channel); + ble_phy_transition_set(ext ? BLE_PHY_TRANSITION_TO_RX : BLE_PHY_TRANSITION_NONE, + BLE_LL_IFS); ble_phy_set_txend_cb(NULL, NULL); - rc = ble_phy_tx(ble_ll_conn_tx_connect_ind_pducb, connsm, - ext ? BLE_PHY_TRANSITION_TX_RX : BLE_PHY_TRANSITION_NONE); + rc = ble_phy_tx(ble_ll_conn_tx_connect_ind_pducb, connsm); if (rc) { ble_ll_conn_send_connect_req_cancel(); return -1; diff --git a/nimble/controller/src/ble_ll_dtm.c b/nimble/controller/src/ble_ll_dtm.c index c7823e0f1a..2ee6024b29 100644 --- a/nimble/controller/src/ble_ll_dtm.c +++ b/nimble/controller/src/ble_ll_dtm.c @@ -244,7 +244,8 @@ ble_ll_dtm_tx_sched_cb(struct ble_ll_sched_item *sch) goto resched; } - rc = ble_phy_tx(ble_ll_tx_mbuf_pducb, ctx->om, BLE_PHY_TRANSITION_NONE); + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); + rc = ble_phy_tx(ble_ll_tx_mbuf_pducb, ctx->om); if (rc) { goto resched; } diff --git a/nimble/controller/src/ble_ll_iso_big.c b/nimble/controller/src/ble_ll_iso_big.c index 774465315e..5b907bc61a 100644 --- a/nimble/controller/src/ble_ll_iso_big.c +++ b/nimble/controller/src/ble_ll_iso_big.c @@ -652,7 +652,8 @@ ble_ll_iso_big_control_tx(struct ble_ll_iso_big *big) ble_phy_set_txend_cb(ble_ll_iso_big_control_txend_cb, big); ble_phy_setchan(chan_idx, big->ctrl_aa, big->crc_init << 8); - rc = ble_phy_tx(ble_ll_iso_big_control_pdu_cb, big, BLE_PHY_TRANSITION_NONE); + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); + rc = ble_phy_tx(ble_ll_iso_big_control_pdu_cb, big); return rc; } @@ -737,9 +738,11 @@ ble_ll_iso_big_subevent_tx(struct ble_ll_iso_big *big) to_tx = (big->tx.subevents_rem > 1) || big->cstf; - rc = ble_phy_tx(ble_ll_iso_big_subevent_pdu_cb, big, - to_tx ? BLE_PHY_TRANSITION_TX_TX - : BLE_PHY_TRANSITION_NONE); + ble_phy_transition_set( + to_tx ? BLE_PHY_TRANSITION_TO_TX_ISO_SUBEVENT : BLE_PHY_TRANSITION_NONE, + big->interleaved ? big->bis_spacing : big->sub_interval); + + rc = ble_phy_tx(ble_ll_iso_big_subevent_pdu_cb, big); return rc; } @@ -836,12 +839,6 @@ ble_ll_iso_big_event_sched_cb(struct ble_ll_sched_item *sch) big->tx.bis = STAILQ_FIRST(&big->bis_q); big->tx.bis->tx.subevent_num = 1; - if (big->interleaved) { - ble_phy_tifs_txtx_set(big->bis_spacing, 0); - } else { - ble_phy_tifs_txtx_set(big->sub_interval, 0); - } - rc = ble_phy_tx_set_start_time(sch->start_time + g_ble_ll_sched_offset_ticks, sch->remainder); if (rc) { diff --git a/nimble/controller/src/ble_ll_scan.c b/nimble/controller/src/ble_ll_scan.c index 0f73fd2e8c..11f9d4faa9 100644 --- a/nimble/controller/src/ble_ll_scan.c +++ b/nimble/controller/src/ble_ll_scan.c @@ -1569,7 +1569,8 @@ ble_ll_scan_send_scan_req(uint8_t pdu_type, uint8_t *rxbuf, ble_ll_scan_req_pdu_prepare(scansm, addrd->adva, addrd->adva_type, rpa_index); - rc = ble_phy_tx(ble_ll_scan_req_tx_pdu_cb, scansm, BLE_PHY_TRANSITION_TX_RX); + ble_phy_transition_set(BLE_PHY_TRANSITION_TO_RX, BLE_LL_IFS); + rc = ble_phy_tx(ble_ll_scan_req_tx_pdu_cb, scansm); if (rc) { return false; } diff --git a/nimble/controller/src/ble_ll_scan_aux.c b/nimble/controller/src/ble_ll_scan_aux.c index 0cb734a240..2071c62198 100644 --- a/nimble/controller/src/ble_ll_scan_aux.c +++ b/nimble/controller/src/ble_ll_scan_aux.c @@ -1206,8 +1206,8 @@ ble_ll_scan_aux_send_scan_req(struct ble_ll_scan_aux_data *aux, * interrupted if scheduler kicks in. */ - rc = ble_phy_tx(ble_ll_scan_aux_scan_req_tx_pdu_cb, aux, - BLE_PHY_TRANSITION_TX_RX); + ble_phy_transition_set(BLE_PHY_TRANSITION_TO_RX, BLE_LL_IFS); + rc = ble_phy_tx(ble_ll_scan_aux_scan_req_tx_pdu_cb, aux); if (rc) { return false; } diff --git a/nimble/drivers/dialog_cmac/src/ble_phy.c b/nimble/drivers/dialog_cmac/src/ble_phy.c index c077f25ff3..3fe68dddde 100644 --- a/nimble/drivers/dialog_cmac/src/ble_phy.c +++ b/nimble/drivers/dialog_cmac/src/ble_phy.c @@ -580,7 +580,7 @@ ble_phy_irq_field_tx_exc_bs_start_4this(void) { CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_BS_START_4THIS_Msk; - if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TX_RX) { + if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TO_RX) { /* * Setup 2nd frame that will start after current one. * -2us offset to adjust for allowed active clock accuracy. @@ -642,7 +642,7 @@ ble_phy_irq_frame_tx_exc_bs_stop(void) /* Clear latched timestamp so we do not have error on next frame */ (void)CMAC->CM_TS1_REG; - if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TX_RX) { + if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TO_RX) { #if MYNEWT_VAL(BLE_LL_PHY) ble_phy_mode_apply(g_ble_phy_data.phy_mode_rx); #endif @@ -665,7 +665,7 @@ ble_phy_irq_frame_tx_exc_phy_to_idle_4this(void) { CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk; - if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TX_RX) { + if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TO_RX) { ble_phy_rx_setup_xcvr(); g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; @@ -1403,7 +1403,7 @@ ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) } int -ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg) { uint8_t *txbuf = g_ble_phy_tx_buf; int rc; @@ -1412,8 +1412,6 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) assert(CMAC->CM_FRAME_1_REG & CMAC_CM_FRAME_1_REG_FRAME_TX_Msk); - g_ble_phy_data.end_transition = end_trans; - /* * Program required fields now so in worst case TX can continue while we * are still preparing header and payload. @@ -1829,3 +1827,9 @@ ble_phy_dtm_carrier(uint8_t rf_channel) } #endif #endif + +void +ble_phy_transition_set(uint8_t trans, uint16_t usecs) +{ + g_ble_phy_data.end_transition = trans; +} diff --git a/nimble/drivers/native/src/ble_phy.c b/nimble/drivers/native/src/ble_phy.c index e1d2e4aa73..6a90030def 100644 --- a/nimble/drivers/native/src/ble_phy.c +++ b/nimble/drivers/native/src/ble_phy.c @@ -241,7 +241,7 @@ ble_phy_isr(void) ble_xcvr_clear_irq(BLE_XCVR_IRQ_F_TX_END); transition = g_ble_phy_data.phy_transition; - if (transition == BLE_PHY_TRANSITION_TX_RX) { + if (transition == BLE_PHY_TRANSITION_TO_RX) { /* Disable the phy */ /* XXX: count no bufs? */ ble_phy_disable(); @@ -420,9 +420,8 @@ ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) return 0; } - int -ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg) { uint8_t hdr_byte; int rc; @@ -440,9 +439,6 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) } else { } - /* Set the PHY transition */ - g_ble_phy_data.phy_transition = end_trans; - /* Set phy state to transmitting and count packet statistics */ g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; ++g_ble_phy_stats.tx_good; @@ -662,6 +658,7 @@ ble_phy_rfclk_disable(void) } void -ble_phy_tifs_txtx_set(uint16_t usecs, uint8_t anchor) +ble_phy_transition_set(uint8_t trans, uint16_t usecs) { + g_ble_phy_data.phy_transition = trans; } diff --git a/nimble/drivers/nrf51/src/ble_phy.c b/nimble/drivers/nrf51/src/ble_phy.c index 955db6351f..21e65906bf 100644 --- a/nimble/drivers/nrf51/src/ble_phy.c +++ b/nimble/drivers/nrf51/src/ble_phy.c @@ -585,7 +585,7 @@ ble_phy_tx_end_isr(void) } transition = g_ble_phy_data.phy_transition; - if (transition == BLE_PHY_TRANSITION_TX_RX) { + if (transition == BLE_PHY_TRANSITION_TO_RX) { /* Packet pointer needs to be reset. */ ble_phy_rx_xcvr_setup(); @@ -1127,7 +1127,7 @@ ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) } int -ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg) { int rc; uint8_t *dptr; @@ -1200,7 +1200,7 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) /* Enable shortcuts for transmit start/end. */ shortcuts = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk; - if (end_trans == BLE_PHY_TRANSITION_TX_RX) { + if (g_ble_phy_data.phy_transition == BLE_PHY_TRANSITION_TO_RX) { shortcuts |= RADIO_SHORTS_DISABLED_RXEN_Msk; } NRF_RADIO->SHORTS = shortcuts; @@ -1216,9 +1216,6 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) dptr[2] = 0; } - /* Set the PHY transition */ - g_ble_phy_data.phy_transition = end_trans; - /* Set transmitted payload length */ g_ble_phy_data.phy_tx_pyld_len = payload_len; @@ -1523,3 +1520,9 @@ ble_phy_rfclk_disable(void) NRF_CLOCK->TASKS_HFCLKSTOP = 1; #endif } + +void +ble_phy_transition_set(uint8_t trans, uint16_t usecs) +{ + g_ble_phy_data.phy_transition = trans; +} diff --git a/nimble/drivers/nrf5x/src/ble_phy.c b/nimble/drivers/nrf5x/src/ble_phy.c index 6f9e051423..088e7022b2 100644 --- a/nimble/drivers/nrf5x/src/ble_phy.c +++ b/nimble/drivers/nrf5x/src/ble_phy.c @@ -133,6 +133,13 @@ extern uint32_t g_nrf_irk_list[]; (NRF_CILEN_BITS << RADIO_PCNF0_CILEN_Pos) | \ (NRF_TERMLEN_BITS << RADIO_PCNF0_TERMLEN_Pos) +#define PHY_TRANS_NONE (0) +#define PHY_TRANS_TO_TX (1) +#define PHY_TRANS_TO_RX (2) + +#define PHY_TRANS_ANCHOR_START (0) +#define PHY_TRANS_ANCHOR_END (1) + /* BLE PHY data structure */ struct ble_phy_obj { @@ -160,12 +167,9 @@ struct ble_phy_obj void *txend_arg; ble_phy_tx_end_func txend_cb; uint32_t phy_start_cputime; -#if MYNEWT_VAL(BLE_PHY_VARIABLE_TIFS) - uint16_t tifs; -#endif - - uint16_t txtx_time_us; - uint8_t txtx_time_anchor; + uint16_t wfr_usecs; + uint16_t tifs_usecs; + uint8_t tifs_anchor; }; static struct ble_phy_obj g_ble_phy_data; @@ -635,14 +639,6 @@ nrf_wait_disabled(void) } } -#if MYNEWT_VAL(BLE_PHY_VARIABLE_TIFS) -void -ble_phy_tifs_set(uint16_t tifs) -{ - g_ble_phy_data.tifs = tifs; -} -#endif - /** * * @@ -847,6 +843,32 @@ ble_phy_set_start_now(void) return 0; } +static void +ble_phy_wfr_enable_at(uint32_t end_time) +{ + /* Enable wait for response PPI */ + NRF_TIMER0->EVENTS_COMPARE[3] = 0; + phy_ppi_wfr_enable(); + nrf_timer_cc_set(NRF_TIMER0, 3, end_time); + + /* + * It may happen that if CPU is halted for a brief moment (e.g. during + * flash, erase or write), TIMER0 already counted past CC[3] and thus wfr + * will not fire as expected. In case this happened, let's just disable + * PPIs for wfr and trigger wfr manually (i.e. disable radio). + * + * Note that the same applies to RX start time set in CC[0] but since it + * should fire earlier than wfr, fixing wfr is enough. + * + * CC[1] is only used as a reference on RX start. We do not need it here, + * so we can use it as a scratch register. + */ + if (timer0_did_miss(3, 1)) { + phy_ppi_wfr_disable(); + nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE); + } +} + /** * Function is used to set PPI so that we can time out waiting for a reception * to occur. This happens for two reasons: we have sent a packet and we are @@ -871,11 +893,7 @@ ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) phy = g_ble_phy_data.phy_cur_phy_mode; -#if MYNEWT_VAL(BLE_PHY_VARIABLE_TIFS) - tifs = g_ble_phy_data.tifs; -#else - tifs = BLE_LL_IFS; -#endif + tifs = g_ble_phy_data.tifs_usecs; if (txrx == BLE_PHY_WFR_ENABLE_TXRX) { /* RX shall start exactly T_IFS after TX end captured in CC[2] */ @@ -913,27 +931,7 @@ ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) /* Adjust for delay between actual access address RX and EVENT_ADDRESS */ end_time += g_ble_phy_t_rxaddrdelay[phy]; - /* Enable wait for response PPI */ - NRF_TIMER0->EVENTS_COMPARE[3] = 0; - phy_ppi_wfr_enable(); - nrf_timer_cc_set(NRF_TIMER0, 3, end_time); - - /* - * It may happen that if CPU is halted for a brief moment (e.g. during - * flash, erase or write), TIMER0 already counted past CC[3] and thus wfr - * will not fire as expected. In case this happened, let's just disable - * PPIs for wfr and trigger wfr manually (i.e. disable radio). - * - * Note that the same applies to RX start time set in CC[0] but since it - * should fire earlier than wfr, fixing wfr is enough. - * - * CC[1] is only used as a reference on RX start. We do not need it here, - * so we can use it as a scratch register. - */ - if (timer0_did_miss(3, 1)) { - phy_ppi_wfr_disable(); - nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE); - } + ble_phy_wfr_enable_at(end_time); } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) @@ -1049,6 +1047,197 @@ ble_phy_rx_xcvr_setup(void) RADIO_INTENSET_DISABLED_Msk); } +static uint32_t +ble_phy_transition_anchor_get(uint8_t tifs_anchor, uint8_t phy_state, uint8_t phy_mode) +{ + uint32_t time; + + if (tifs_anchor == PHY_TRANS_ANCHOR_END) { + /* END timestamp is captured in CC[2] */ + time = NRF_TIMER0->CC[2]; + + /* Adjust for delay between EVENT_END and actual TX/RX end time */ + time += (phy_state == BLE_PHY_STATE_TX) + ? g_ble_phy_t_txenddelay[phy_mode] + : -g_ble_phy_t_rxenddelay[phy_mode]; + + } else { + /* ADDRESS timestamp is captured in CC[1] */ + time = NRF_TIMER0->CC[1]; + + /* Adjust for delay between EVENT_ADDRESS and actual AA time ota */ + time += (phy_state == BLE_PHY_STATE_TX) + ? g_ble_phy_t_txaddrdelay[phy_mode] + : -g_ble_phy_t_rxaddrdelay[phy_mode]; + + /* Adjust by sync word length to get TX/RX start time */ + time -= ble_ll_pdu_syncword_us(phy_mode); + } + + return time; +} + +static int +ble_transition_to_tx(uint8_t tifs_anchor, uint16_t tifs_usecs, uint8_t phy_state) +{ + uint32_t anchor_time; + uint32_t radio_time; + uint32_t start_time; + bool is_late; + uint8_t next_phy_mode; + uint8_t prev_phy_mode = g_ble_phy_data.phy_cur_phy_mode; + +#if MYNEWT_VAL(BLE_LL_PHY) + ble_phy_mode_apply(g_ble_phy_data.phy_tx_phy_mode); +#endif + next_phy_mode = g_ble_phy_data.phy_cur_phy_mode; + + anchor_time = + ble_phy_transition_anchor_get(tifs_anchor, phy_state, prev_phy_mode); + start_time = anchor_time + tifs_usecs; + radio_time = start_time; + +#if PHY_USE_FEM_PA + fem_time = anchor_time - MYNEWT_VAL(BLE_FEM_PA_TURN_ON_US); + NRF_TIMER0->EVENTS_COMPARE[2] = 0; + phy_fem_enable_pa(); + nrf_timer_cc_set(NRF_TIMER0, 2, fem_time); +#endif + + /* Adjust for TX rump-up */ + radio_time -= BLE_PHY_T_TXENFAST; + /* Adjust for delay between EVENT_READY and actual TX start time */ + radio_time -= g_ble_phy_t_txdelay[next_phy_mode]; + + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + phy_ppi_timer0_compare0_to_radio_txen_enable(); + nrf_timer_cc_set(NRF_TIMER0, 0, radio_time); + + /* Need to check if TIMER0 did not already count past CC[0] and/or CC[2], so + * we're not stuck waiting for events in case radio and/or PA was not + * started. If event was triggered we're fine regardless of timer value. + * + * Note: CC[3] is used only for wfr which we do not need here. + */ + is_late = timer0_did_miss(0, 3); +#if PHY_USE_FEM_PA + is_late = is_late || timer0_did_miss(2, 3); +#endif + if (is_late) { + g_ble_phy_data.phy_transition_late = 1; + + return 1; + } + + return 0; +} + +static int +ble_transition_to_rx(uint8_t tifs_anchor, uint16_t tifs_usecs, + uint16_t wfr_usecs, uint8_t phy_state) +{ + uint32_t anchor_time; + uint32_t radio_time; + uint32_t start_time; + uint32_t wfr_time; + uint8_t next_phy_mode; + uint8_t prev_phy_mode = g_ble_phy_data.phy_cur_phy_mode; + +#if MYNEWT_VAL(BLE_LL_PHY) + ble_phy_mode_apply(g_ble_phy_data.phy_rx_phy_mode); +#endif + /* Packet pointer needs to be reset. */ + ble_phy_rx_xcvr_setup(); + next_phy_mode = g_ble_phy_data.phy_cur_phy_mode; + + anchor_time = + ble_phy_transition_anchor_get(tifs_anchor, phy_state, prev_phy_mode); + start_time = anchor_time + tifs_usecs; + radio_time = start_time; + +#if PHY_USE_FEM_LNA + fem_time = anchor_time - MYNEWT_VAL(BLE_FEM_LNA_TURN_ON_US); + NRF_TIMER0->EVENTS_COMPARE[2] = 0; + phy_fem_enable_lna(); + nrf_timer_cc_set(NRF_TIMER0, 2, fem_time); +#endif + + /* Adjust for RX rump-up */ + radio_time -= BLE_PHY_T_RXENFAST; + /* Start listening a bit earlier due to allowed active clock accuracy */ + radio_time -= 2; + + /* Setup wfr relative to expected radio/PDU start */ + wfr_time = start_time; + /* Add amount of usecs to wait */ + wfr_time += wfr_usecs; + /* Adjust for receiving access address since this triggers EVENT_ADDRESS */ + wfr_time += ble_phy_mode_pdu_start_off(next_phy_mode); + /* Adjust for delay between actual access address RX and EVENT_ADDRESS */ + wfr_time += g_ble_phy_t_rxaddrdelay[next_phy_mode]; + /* Wait a bit longer due to allowed active clock accuracy */ + wfr_time += 2; + /* + * It's possible that we'll capture PDU start time at the end of timer + * cycle and since wfr expires at the beginning of calculated timer + * cycle it can be almost 1 usec too early. Let's compensate for this + * by waiting 1 usec more. + */ + wfr_time += 1; + wfr_time += MYNEWT_VAL(BLE_PHY_EXTENDED_TIFS); + + ble_phy_wfr_enable_at(wfr_time); + + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + phy_ppi_timer0_compare0_to_radio_rxen_enable(); + nrf_timer_cc_set(NRF_TIMER0, 0, radio_time); + + /* In case TIMER0 did already count past CC[0] and/or CC[2], radio + * and/or LNA may not be enabled. In any case we won't be stuck since + * wfr will cancel rx if needed. + * + * FIXME failing to enable LNA may result in unexpected RSSI drop in + * case we still rxd something, so perhaps we could check it here + */ + + return 0; +} + +static int +ble_transition_to_none(void) +{ + nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); + NRF_TIMER0->TASKS_SHUTDOWN = 1; + phy_ppi_wfr_disable(); + phy_ppi_timer0_compare0_to_radio_txen_disable(); + phy_ppi_rtc0_compare0_to_timer0_start_disable(); + ble_phy_disable(); + phy_ppi_fem_disable(); + + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); + + return 0; +} + +static int +ble_phy_transition(uint8_t transition, uint8_t tifs_anchor, + uint16_t tifs_usecs, uint16_t wfr_usecs, uint8_t phy_state) +{ + int rc = 1; + + if (transition == PHY_TRANS_TO_TX) { + rc = ble_transition_to_tx(tifs_anchor, tifs_usecs, phy_state); + } else if (transition == PHY_TRANS_TO_RX) { + rc = ble_transition_to_rx(tifs_anchor, tifs_usecs, wfr_usecs, phy_state); + } + + if (rc) { + ble_transition_to_none(); + } + + return 0; +} + /** * Called from interrupt context when the transmit ends * @@ -1056,19 +1245,11 @@ ble_phy_rx_xcvr_setup(void) static void ble_phy_tx_end_isr(void) { - uint8_t tx_phy_mode; uint8_t was_encrypted; uint8_t transition; - uint32_t rx_time; - uint32_t tx_time; -#if PHY_USE_FEM - uint32_t fem_time; -#endif - uint32_t radio_time; - uint16_t tifs; - - /* Store PHY on which we've just transmitted smth */ - tx_phy_mode = g_ble_phy_data.phy_cur_phy_mode; + uint16_t wfr_usecs; + uint16_t tifs_usecs; + uint8_t tifs_anchor; /* If this transmission was encrypted we need to remember it */ was_encrypted = g_ble_phy_data.phy_encrypted; @@ -1090,109 +1271,18 @@ ble_phy_tx_end_isr(void) } #endif -#if MYNEWT_VAL(BLE_PHY_VARIABLE_TIFS) - tifs = g_ble_phy_data.tifs; - g_ble_phy_data.tifs = BLE_LL_IFS; -#else - tifs = BLE_LL_IFS; -#endif transition = g_ble_phy_data.phy_transition; + wfr_usecs = g_ble_phy_data.wfr_usecs; + tifs_usecs = g_ble_phy_data.tifs_usecs; + tifs_anchor = g_ble_phy_data.tifs_anchor; + + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); if (g_ble_phy_data.txend_cb) { g_ble_phy_data.txend_cb(g_ble_phy_data.txend_arg); } - if (transition == BLE_PHY_TRANSITION_TX_RX) { -#if MYNEWT_VAL(BLE_LL_PHY) - ble_phy_mode_apply(g_ble_phy_data.phy_rx_phy_mode); -#endif - - /* Packet pointer needs to be reset. */ - ble_phy_rx_xcvr_setup(); - - ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_TXRX, tx_phy_mode, 0); - - /* Schedule RX exactly T_IFS after TX end captured in CC[2] */ - rx_time = NRF_TIMER0->CC[2] + tifs; - /* Adjust for delay between EVENT_END and actual TX end time */ - rx_time += g_ble_phy_t_txenddelay[tx_phy_mode]; - /* Start listening a bit earlier due to allowed active clock accuracy */ - rx_time -= 2; - -#if PHY_USE_FEM_LNA - fem_time = rx_time - MYNEWT_VAL(BLE_FEM_LNA_TURN_ON_US); - NRF_TIMER0->EVENTS_COMPARE[2] = 0; - phy_fem_enable_lna(); - nrf_timer_cc_set(NRF_TIMER0, 2, fem_time); -#endif - - radio_time = rx_time - BLE_PHY_T_RXENFAST; - NRF_TIMER0->EVENTS_COMPARE[0] = 0; - phy_ppi_timer0_compare0_to_radio_rxen_enable(); - nrf_timer_cc_set(NRF_TIMER0, 0, radio_time); - - /* In case TIMER0 did already count past CC[0] and/or CC[2], radio - * and/or LNA may not be enabled. In any case we won't be stuck since - * wfr will cancel rx if needed. - * - * FIXME failing to enable LNA may result in unexpected RSSI drop in - * case we still rxd something, so perhaps we could check it here - */ - } else if (transition == BLE_PHY_TRANSITION_TX_TX) { - if (g_ble_phy_data.txtx_time_anchor) { - /* Calculate TX anchor relative to current TX end */ - - /* TX end timestamp is captured in CC[2] */ - tx_time = NRF_TIMER0->CC[2]; - /* Adjust for delay between EVENT_END and actual TX end time */ - tx_time += g_ble_phy_t_txenddelay[tx_phy_mode]; - } else { - /* Calculate TX anchor relative to current TX start */ - - /* AA timestamp is captured in CC[1] */ - tx_time = NRF_TIMER0->CC[1]; - /* Adjust for delay between EVENT_ADDRESS and actual AA time ota */ - tx_time += g_ble_phy_t_txaddrdelay[tx_phy_mode]; - /* Adjust by sync word length to get TX start time */ - tx_time -= ble_ll_pdu_syncword_us(tx_phy_mode); - } - - tx_time += g_ble_phy_data.txtx_time_us; - -#if PHY_USE_FEM_PA - fem_time = tx_time - MYNEWT_VAL(BLE_FEM_PA_TURN_ON_US); -#endif - - /* Adjust for delay between EVENT_READY and actual TX start time */ - tx_time -= g_ble_phy_t_txdelay[g_ble_phy_data.phy_cur_phy_mode]; - - radio_time = tx_time - BLE_PHY_T_TXENFAST; - NRF_TIMER0->EVENTS_COMPARE[0] = 0; - phy_ppi_timer0_compare0_to_radio_txen_enable(); - nrf_timer_cc_set(NRF_TIMER0, 0, radio_time); - -#if PHY_USE_FEM_PA - NRF_TIMER0->EVENTS_COMPARE[2] = 0; - phy_fem_enable_pa(); - nrf_timer_cc_set(NRF_TIMER0, 2, fem_time); -#endif - - if (timer0_did_miss(0, 3)) { - phy_ppi_timer0_compare0_to_radio_txen_disable(); - g_ble_phy_data.phy_transition_late = 1; - } - } else { - /* - * XXX: not sure we need to stop the timer here all the time. Or that - * it should be stopped here. - */ - nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); - NRF_TIMER0->TASKS_SHUTDOWN = 1; - phy_ppi_wfr_disable(); - phy_ppi_timer0_compare0_to_radio_txen_disable(); - phy_ppi_rtc0_compare0_to_timer0_start_disable(); - assert(transition == BLE_PHY_TRANSITION_NONE); - } + ble_phy_transition(transition, tifs_anchor, tifs_usecs, wfr_usecs, BLE_PHY_STATE_TX); } static inline uint8_t @@ -1225,15 +1315,8 @@ ble_phy_rx_end_isr(void) { int rc; uint8_t *dptr; - uint8_t crcok; - uint32_t tx_time; -#if PHY_USE_FEM_PA - uint32_t fem_time; -#endif - uint32_t radio_time; - uint16_t tifs; struct ble_mbuf_hdr *ble_hdr; - bool is_late; + uint8_t crcok; /* Disable automatic RXEN */ phy_ppi_timer0_compare0_to_radio_rxen_disable(); @@ -1278,10 +1361,6 @@ ble_phy_rx_end_isr(void) #endif } -#if MYNEWT_VAL(BLE_LL_PHY) - ble_phy_mode_apply(g_ble_phy_data.phy_tx_phy_mode); -#endif - /* * Let's schedule TX now and we will just cancel it after processing RXed * packet if we don't need TX. @@ -1295,51 +1374,16 @@ ble_phy_rx_end_isr(void) * during radio ramp-up - this gives us extra 40 usecs which is more than * enough. */ + if (g_ble_phy_data.phy_transition == PHY_TRANS_NONE) { + /* XXX: Should be removed after finding all missing uses of ble_phy_transition_set */ + g_ble_phy_data.phy_transition = PHY_TRANS_TO_TX; + } -#if MYNEWT_VAL(BLE_PHY_VARIABLE_TIFS) - tifs = g_ble_phy_data.tifs; - g_ble_phy_data.tifs = BLE_LL_IFS; -#else - tifs = BLE_LL_IFS; -#endif - - /* Schedule TX exactly T_IFS after RX end captured in CC[2] */ - tx_time = NRF_TIMER0->CC[2] + tifs; - /* Adjust for delay between actual RX end time and EVENT_END */ - tx_time -= g_ble_phy_t_rxenddelay[ble_hdr->rxinfo.phy_mode]; - -#if PHY_USE_FEM_PA - fem_time = tx_time - MYNEWT_VAL(BLE_FEM_PA_TURN_ON_US); -#endif - - /* Adjust for delay between EVENT_READY and actual TX start time */ - tx_time -= g_ble_phy_t_txdelay[g_ble_phy_data.phy_cur_phy_mode]; + ble_phy_transition(g_ble_phy_data.phy_transition, + g_ble_phy_data.tifs_anchor, g_ble_phy_data.tifs_usecs, + g_ble_phy_data.wfr_usecs, BLE_PHY_STATE_RX); - radio_time = tx_time - BLE_PHY_T_TXENFAST; - NRF_TIMER0->EVENTS_COMPARE[0] = 0; - phy_ppi_timer0_compare0_to_radio_txen_enable(); - nrf_timer_cc_set(NRF_TIMER0, 0, radio_time); - -#if PHY_USE_FEM_PA - NRF_TIMER0->EVENTS_COMPARE[2] = 0; - phy_fem_enable_pa(); - nrf_timer_cc_set(NRF_TIMER0, 2, fem_time); -#endif - - /* Need to check if TIMER0 did not already count past CC[0] and/or CC[2], so - * we're not stuck waiting for events in case radio and/or PA was not - * started. If event was triggered we're fine regardless of timer value. - * - * Note: CC[3] is used only for wfr which we do not need here. - */ - is_late = timer0_did_miss(0, 3); -#if PHY_USE_FEM_PA - is_late = is_late || timer0_did_miss(2, 3); -#endif - if (is_late) { - phy_ppi_timer0_compare0_to_radio_txen_disable(); - g_ble_phy_data.phy_transition_late = 1; - } + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); /* * XXX: This is a horrible ugly hack to deal with the RAM S1 byte @@ -1593,9 +1637,7 @@ ble_phy_init(void) /* Set phy channel to an invalid channel so first set channel works */ g_ble_phy_data.phy_chan = BLE_PHY_NUM_CHANS; -#if MYNEWT_VAL(BLE_PHY_VARIABLE_TIFS) - g_ble_phy_data.tifs = BLE_LL_IFS; -#endif + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); /* Toggle peripheral power to reset (just in case) */ nrf_radio_power_set(NRF_RADIO, false); @@ -1921,7 +1963,7 @@ ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) } int -ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg) { int rc; uint8_t *dptr; @@ -1930,6 +1972,7 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) uint8_t hdr_byte; uint32_t state; uint32_t shortcuts; + uint8_t end_trans; if (g_ble_phy_data.phy_transition_late) { ble_phy_disable(); @@ -1937,6 +1980,8 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) return BLE_PHY_ERR_TX_LATE; } + end_trans = g_ble_phy_data.phy_transition; + /* * This check is to make sure that the radio is not in a state where * it is moving to disabled state. If so, let it get there. @@ -2231,6 +2276,7 @@ ble_phy_disable(void) ble_phy_disable_irq_and_ppi(); g_ble_phy_data.phy_transition_late = 0; + ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); #if PHY_USE_FEM phy_fem_disable(); @@ -2369,8 +2415,35 @@ ble_phy_rfclk_disable(void) } void -ble_phy_tifs_txtx_set(uint16_t usecs, uint8_t anchor) +ble_phy_transition_set(uint8_t trans, uint16_t usecs) +{ + uint8_t transition; + uint8_t anchor; + + if (trans == BLE_PHY_TRANSITION_TO_TX_ISO_SUBEVENT || + trans == BLE_PHY_TRANSITION_TO_RX_ISO_SUBEVENT) { + anchor = PHY_TRANS_ANCHOR_START; + } else { + anchor = PHY_TRANS_ANCHOR_END; + } + + if (trans == BLE_PHY_TRANSITION_TO_RX || + trans == BLE_PHY_TRANSITION_TO_RX_ISO_SUBEVENT) { + transition = PHY_TRANS_TO_RX; + } else if (trans == BLE_PHY_TRANSITION_TO_TX || + trans == BLE_PHY_TRANSITION_TO_TX_ISO_SUBEVENT) { + transition = PHY_TRANS_TO_TX; + } else { + transition = PHY_TRANS_NONE; + } + + g_ble_phy_data.phy_transition = transition; + g_ble_phy_data.tifs_anchor = anchor; + g_ble_phy_data.tifs_usecs = usecs ? usecs : BLE_LL_IFS; +} + +void +ble_phy_wfr_set(uint16_t usecs) { - g_ble_phy_data.txtx_time_us = usecs; - g_ble_phy_data.txtx_time_anchor = anchor; + g_ble_phy_data.wfr_usecs = usecs; } From e29f0e155eb956e559a2abd9a74a5bada8d1ee91 Mon Sep 17 00:00:00 2001 From: Mariusz Skamra Date: Fri, 8 Aug 2025 14:37:34 +0200 Subject: [PATCH 2/6] nimble/ll: Introduce Rx end early callback This adds Rx end early callback that is called before scheduled PHY transition. This allows user to e.g. prepare CCM for next Rx. --- nimble/controller/include/controller/ble_ll.h | 3 +++ nimble/controller/src/ble_ll.c | 24 +++++++++++++++++++ nimble/drivers/nrf5x/src/ble_phy.c | 20 ++++++++++------ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/nimble/controller/include/controller/ble_ll.h b/nimble/controller/include/controller/ble_ll.h index ab75edcba3..9fa32d6545 100644 --- a/nimble/controller/include/controller/ble_ll.h +++ b/nimble/controller/include/controller/ble_ll.h @@ -581,6 +581,9 @@ int ble_ll_rx_start(uint8_t *rxbuf, uint8_t chan, struct ble_mbuf_hdr *hdr); /* Called by the PHY when a packet reception ends */ int ble_ll_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr); +/* Called by the PHY when a packet reception ends */ +int ble_ll_rx_early_end(const uint8_t *rxbuf, const struct ble_mbuf_hdr *rxhdr); + /* Helper callback to tx mbuf using ble_phy_tx() */ uint8_t ble_ll_tx_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte); uint8_t ble_ll_tx_flat_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte); diff --git a/nimble/controller/src/ble_ll.c b/nimble/controller/src/ble_ll.c index a0d6dbbc03..3eda627b0a 100644 --- a/nimble/controller/src/ble_ll.c +++ b/nimble/controller/src/ble_ll.c @@ -1305,6 +1305,30 @@ ble_ll_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) return rc; } +/** + * Early callback called by the PHY when a packet reception has ended. + * + * NOTE: Called from interrupt context! + * Avoid time-consuming operations, such as buffer copying or memory allocation. + * + * @param rxbuf Pointer to received PDU data + * rxhdr Pointer to BLE header of received mbuf + * + * @return int + * < 0: Disable the phy after reception. + * == 0: Success. Do not disable the PHY. + * > 0: Do not disable PHY as that has already been done. + */ +int +ble_ll_rx_early_end(const uint8_t *rxbuf, const struct ble_mbuf_hdr *rxhdr) +{ + int rc = 0; + + /* TODO */ + + return rc; +} + uint8_t ble_ll_tx_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) { diff --git a/nimble/drivers/nrf5x/src/ble_phy.c b/nimble/drivers/nrf5x/src/ble_phy.c index 088e7022b2..0cc94286d9 100644 --- a/nimble/drivers/nrf5x/src/ble_phy.c +++ b/nimble/drivers/nrf5x/src/ble_phy.c @@ -1361,6 +1361,19 @@ ble_phy_rx_end_isr(void) #endif } + /* + * XXX: This is a horrible ugly hack to deal with the RAM S1 byte + * that is not sent over the air but is present here. Simply move the + * data pointer to deal with it. Fix this later. + */ + dptr[2] = dptr[1]; + dptr[1] = dptr[0]; + rc = ble_ll_rx_early_end(dptr + 1, ble_hdr); + if (rc < 0) { + ble_phy_disable(); + return; + } + /* * Let's schedule TX now and we will just cancel it after processing RXed * packet if we don't need TX. @@ -1385,13 +1398,6 @@ ble_phy_rx_end_isr(void) ble_phy_transition_set(BLE_PHY_TRANSITION_NONE, 0); - /* - * XXX: This is a horrible ugly hack to deal with the RAM S1 byte - * that is not sent over the air but is present here. Simply move the - * data pointer to deal with it. Fix this later. - */ - dptr[2] = dptr[1]; - dptr[1] = dptr[0]; rc = ble_ll_rx_end(dptr + 1, ble_hdr); if (rc < 0) { ble_phy_disable(); From c385ceeead6b74eda0335423115e824ea34b8eb1 Mon Sep 17 00:00:00 2001 From: Magdalena Kasenberg Date: Thu, 26 Sep 2024 16:16:59 +0200 Subject: [PATCH 3/6] nimble/transport: Add support for IPC ICBMsg transport Support for nRF5340. --- nimble/controller/src/ble_ll.c | 2 + nimble/controller/syscfg.yml | 8 + .../include/nimble/transport/hci_ipc.h | 23 +- nimble/transport/common/hci_ipc/src/hci_ipc.c | 35 ++- nimble/transport/common/hci_ipc/syscfg.yml | 24 ++ nimble/transport/ipc/icbmsg/pkg.yml | 38 ++++ .../transport/ipc/icbmsg/src/icbmsg_ble_hci.c | 208 ++++++++++++++++++ nimble/transport/ipc/icbmsg/syscfg.yml | 16 ++ nimble/transport/ipc/pkg.yml | 37 ++++ nimble/transport/ipc/syscfg.yml | 36 +++ nimble/transport/pkg.yml | 2 + nimble/transport/syscfg.yml | 2 + 12 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 nimble/transport/common/hci_ipc/syscfg.yml create mode 100644 nimble/transport/ipc/icbmsg/pkg.yml create mode 100644 nimble/transport/ipc/icbmsg/src/icbmsg_ble_hci.c create mode 100644 nimble/transport/ipc/icbmsg/syscfg.yml create mode 100644 nimble/transport/ipc/pkg.yml create mode 100644 nimble/transport/ipc/syscfg.yml diff --git a/nimble/controller/src/ble_ll.c b/nimble/controller/src/ble_ll.c index 3eda627b0a..24a9849d90 100644 --- a/nimble/controller/src/ble_ll.c +++ b/nimble/controller/src/ble_ll.c @@ -2057,7 +2057,9 @@ void ble_transport_ll_init(void) { /* Tell the host that we are ready to receive packets */ +#if MYNEWT_VAL(BLE_LL_HCI_NOOP_AFTER_INIT) ble_ll_hci_send_noop(); +#endif } int diff --git a/nimble/controller/syscfg.yml b/nimble/controller/syscfg.yml index 517cfe0fdf..a7b75d83fb 100644 --- a/nimble/controller/syscfg.yml +++ b/nimble/controller/syscfg.yml @@ -226,6 +226,11 @@ syscfg.defs: Enables LLCP tracing using HCI vendor-specific events. value: '0' + BLE_LL_HCI_NOOP_AFTER_INIT: + description: > + Enables sending the NO-OP opcode HCI event after LL init. + value: '1' + # Configuration for LL supported features. # # There are a total 8 features that the LL can support. These can be found @@ -685,6 +690,9 @@ syscfg.vals.BLE_LL_ISO_BROADCASTER: syscfg.vals.'!BLE_HOST && !BABBLESIM': BLE_LL_HCI_VS_EVENT_ON_ASSERT: 1 +syscfg.vals.'BLE_TRANSPORT_HS=="ipc"': + BLE_LL_HCI_NOOP_AFTER_INIT: 0 + syscfg.restrictions: - BLE_TRANSPORT_LL == "native" - BLE_LL_PUBLIC_DEV_ADDR <= 0xffffffffffff diff --git a/nimble/transport/common/hci_ipc/include/nimble/transport/hci_ipc.h b/nimble/transport/common/hci_ipc/include/nimble/transport/hci_ipc.h index 551fb93d4c..599c28d545 100644 --- a/nimble/transport/common/hci_ipc/include/nimble/transport/hci_ipc.h +++ b/nimble/transport/common/hci_ipc/include/nimble/transport/hci_ipc.h @@ -21,13 +21,26 @@ #define _HCI_IPC_H_ #include - +#include + +#if MYNEWT_VAL(IPC_ICBMSG) +#include "nimble/hci_common.h" +#define HCI_IPC_TYPE_CMD 0x01 +#define HCI_IPC_TYPE_ACL 0x02 +/* #define HCI_IPC_TYPE_SCO 0x03 */ +#define HCI_IPC_TYPE_EVT 0x04 +#define HCI_IPC_TYPE_ISO 0x05 +/* These two are not used actually */ +#define HCI_IPC_TYPE_EVT_DISCARDABLE 0x06 +#define HCI_IPC_TYPE_EVT_IN_CMD 0x07 +#else #define HCI_IPC_TYPE_CMD 0x01 #define HCI_IPC_TYPE_ACL 0x02 #define HCI_IPC_TYPE_EVT 0x04 #define HCI_IPC_TYPE_EVT_DISCARDABLE 0x05 #define HCI_IPC_TYPE_EVT_IN_CMD 0x06 #define HCI_IPC_TYPE_ISO 0x07 +#endif struct __attribute__((packed)) hci_ipc_hdr { uint8_t type; @@ -55,15 +68,20 @@ struct hci_ipc_shm { void hci_ipc_init(volatile struct hci_ipc_shm *shm, struct hci_ipc_sm *sm); int hci_ipc_rx(struct hci_ipc_sm *sm, const uint8_t *buf, uint16_t len); +#if !MYNEWT_VAL(IPC_ICBMSG) extern void hci_ipc_atomic_put(volatile uint16_t *num); extern uint16_t hci_ipc_atomic_get(volatile uint16_t *num); /* Just to optimize static inlines below, do not use directly! */ extern volatile struct hci_ipc_shm *g_ipc_shm; +#endif static inline int hci_ipc_get(uint8_t type) { +#if MYNEWT_VAL(IPC_ICBMSG) + return 1; +#else volatile struct hci_ipc_shm *shm = g_ipc_shm; switch (type) { @@ -76,11 +94,13 @@ hci_ipc_get(uint8_t type) } return 0; +#endif } static inline void hci_ipc_put(uint8_t type) { +#if !MYNEWT_VAL(IPC_ICBMSG) volatile struct hci_ipc_shm *shm = g_ipc_shm; switch (type) { @@ -94,6 +114,7 @@ hci_ipc_put(uint8_t type) hci_ipc_atomic_put(&shm->n2a_num_evt_disc); break; } +#endif } #endif /* _HCI_IPC_H_ */ diff --git a/nimble/transport/common/hci_ipc/src/hci_ipc.c b/nimble/transport/common/hci_ipc/src/hci_ipc.c index f1ebbd8953..3219b9835f 100644 --- a/nimble/transport/common/hci_ipc/src/hci_ipc.c +++ b/nimble/transport/common/hci_ipc/src/hci_ipc.c @@ -87,7 +87,11 @@ hci_ipc_alloc(struct hci_ipc_sm *sm) static bool hci_ipc_has_hdr(struct hci_ipc_sm *sm) { +#if MYNEWT_VAL(IPC_ICBMSG) + return sm->hdr_len == sizeof(sm->hdr.type); +#else return sm->hdr_len == sizeof(sm->hdr); +#endif } static void @@ -137,9 +141,35 @@ hci_ipc_frame(struct hci_ipc_sm *sm) sm->buf = NULL; } +#if MYNEWT_VAL(IPC_ICBMSG) +static uint16_t +hci_ipc_get_pkt_size(const uint8_t *buf, uint8_t type) +{ + switch (type) { + case HCI_IPC_TYPE_ACL: + return sizeof(struct hci_data_hdr) + + get_le16(&((struct hci_data_hdr *)buf)->hdh_len); + case HCI_IPC_TYPE_EVT: + return sizeof(struct ble_hci_ev) + ((struct ble_hci_ev *)buf)->length; + case HCI_IPC_TYPE_CMD: + return sizeof(struct ble_hci_cmd) + ((struct ble_hci_cmd *)buf)->length; + case HCI_IPC_TYPE_ISO: + return sizeof(struct ble_hci_iso) + + get_le16(&((struct ble_hci_iso *)buf)->length); + } + + return 0; +} +#endif + static uint16_t hci_ipc_copy_to_hdr(struct hci_ipc_sm *sm, const uint8_t *buf, uint16_t len) { +#if MYNEWT_VAL(IPC_ICBMSG) + len = 1; + sm->hdr.type = buf[0]; + sm->hdr.length = hci_ipc_get_pkt_size(buf + 1, sm->hdr.type); +#else uint16_t rem_hdr_len; uint8_t *p; @@ -152,6 +182,7 @@ hci_ipc_copy_to_hdr(struct hci_ipc_sm *sm, const uint8_t *buf, uint16_t len) p = (void *)&sm->hdr; memcpy(p + sm->hdr_len, buf, len); +#endif sm->hdr_len += len; @@ -225,10 +256,11 @@ hci_ipc_rx(struct hci_ipc_sm *sm, const uint8_t *buf, uint16_t len) void hci_ipc_init(volatile struct hci_ipc_shm *shm, struct hci_ipc_sm *sm) { + memset(sm, 0, sizeof(*sm)); +#if !MYNEWT_VAL(IPC_ICBMSG) assert(g_ipc_shm == NULL); g_ipc_shm = shm; - memset(sm, 0, sizeof(*sm)); #if MYNEWT_VAL(BLE_CONTROLLER) while (shm->n2a_num_evt_disc == 0) { @@ -239,4 +271,5 @@ hci_ipc_init(volatile struct hci_ipc_shm *shm, struct hci_ipc_sm *sm) shm->n2a_num_evt = MYNEWT_VAL(BLE_TRANSPORT_EVT_COUNT); shm->n2a_num_evt_disc = MYNEWT_VAL(BLE_TRANSPORT_EVT_DISCARDABLE_COUNT); #endif +#endif } diff --git a/nimble/transport/common/hci_ipc/syscfg.yml b/nimble/transport/common/hci_ipc/syscfg.yml new file mode 100644 index 0000000000..b160faaf91 --- /dev/null +++ b/nimble/transport/common/hci_ipc/syscfg.yml @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +syscfg.defs: + IPC_ICBMSG: + description: 'Enables ICBMsg backend for IPC.' + value: 0 + +syscfg.vals.'BLE_TRANSPORT_LL=="ipc" || BLE_TRANSPORT_HS=="ipc"': + IPC_ICBMSG: 1 diff --git a/nimble/transport/ipc/icbmsg/pkg.yml b/nimble/transport/ipc/icbmsg/pkg.yml new file mode 100644 index 0000000000..bd0a305bb6 --- /dev/null +++ b/nimble/transport/ipc/icbmsg/pkg.yml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: nimble/transport/ipc/icbmsg +pkg.description: HCI transport via IPC with ICBMsg backend +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - ipc + - icbmsg + +pkg.deps: + - nimble + - nimble/transport/common/hci_ipc + - "@apache-mynewt-core/hw/drivers/ipc/icbmsg" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/hw/drivers/ipc" + +pkg.apis: + - ble_transport diff --git a/nimble/transport/ipc/icbmsg/src/icbmsg_ble_hci.c b/nimble/transport/ipc/icbmsg/src/icbmsg_ble_hci.c new file mode 100644 index 0000000000..d120260173 --- /dev/null +++ b/nimble/transport/ipc/icbmsg/src/icbmsg_ble_hci.c @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLE_HCI_IPC_ID (0) + +static struct hci_ipc_sm g_hci_ipc_sm; + +static void ble_hci_trans_rx(const void *data, size_t len, void *user_data); +static struct ipc_ept_cfg hci_ept_cfg = { + .name = "nrf_bt_hci", + .cb = { + .received = ble_hci_trans_rx, + }, + .tx_channel = MYNEWT_VAL(BLE_TRANSPORT_IPC_TX_CHANNEL), + .rx_channel = MYNEWT_VAL(BLE_TRANSPORT_IPC_RX_CHANNEL), +}; +static uint8_t hci_ept_local_addr; + +static int +icbmsg_ble_hci_send_mbuf(uint8_t type, struct os_mbuf *om) +{ + int rc; + struct os_mbuf *x; + struct ipc_icmsg_buf buf; + + rc = ipc_icbmsg_alloc_tx_buf(BLE_HCI_IPC_ID, &buf, 1 + OS_MBUF_PKTHDR(om)->omp_len); + assert(rc == 0); + if (rc != 0) { + return BLE_ERR_MEM_CAPACITY; + } + + buf.data[0] = type; + buf.len = 1; + + x = om; + while (x) { + memcpy(buf.data + buf.len, x->om_data, x->om_len); + buf.len += x->om_len; + x = SLIST_NEXT(x, om_next); + } + + rc = ipc_icbmsg_send_buf(BLE_HCI_IPC_ID, hci_ept_local_addr, &buf); + + os_mbuf_free_chain(om); + + return (rc < 0) ? BLE_ERR_MEM_CAPACITY : 0; +} + +static int +icbmsg_ble_hci_acl_tx(struct os_mbuf *om) +{ + return icbmsg_ble_hci_send_mbuf(HCI_IPC_TYPE_ACL, om); +} + +#if !MYNEWT_VAL(BLE_CONTROLLER) +static int +icbmsg_ble_hci_iso_tx(struct os_mbuf *om) +{ + return icbmsg_ble_hci_send_mbuf(HCI_IPC_TYPE_ISO, om); +} +#endif + +static void +ble_hci_trans_rx(const void *data, size_t len, void *user_data) +{ + hci_ipc_rx(&g_hci_ipc_sm, data, len); +} + +static void +icbmsg_ble_hci_init(void) +{ + os_sr_t sr; + + SYSINIT_ASSERT_ACTIVE(); + + OS_ENTER_CRITICAL(sr); + hci_ept_local_addr = ipc_icmsg_register_ept(BLE_HCI_IPC_ID, &hci_ept_cfg); + OS_EXIT_CRITICAL(sr); + + while (!ipc_icsmsg_ept_ready(BLE_HCI_IPC_ID, hci_ept_local_addr)) { + os_cputime_delay_usecs(1000); + ipc_process_signal(BLE_HCI_IPC_ID); + } +} + +#if MYNEWT_VAL(BLE_CONTROLLER) +int +ble_transport_to_hs_evt_impl(void *ev_buf) +{ + int rc; + uint8_t *hci_ev = ev_buf; + struct ipc_icmsg_buf buf; + uint16_t length; + uint8_t type = HCI_IPC_TYPE_EVT; + + /* struct ble_hci_ev */ + length = 2 + hci_ev[1]; + + rc = ipc_icbmsg_alloc_tx_buf(BLE_HCI_IPC_ID, &buf, 1 + length); + assert(rc == 0); + if (rc != 0) { + return BLE_ERR_MEM_CAPACITY; + } + + buf.data[0] = type; + buf.len = 1; + + memcpy(buf.data + buf.len, hci_ev, length); + buf.len += length; + + rc = ipc_icbmsg_send_buf(BLE_HCI_IPC_ID, hci_ept_local_addr, &buf); + + ble_transport_ipc_free(ev_buf); + + return (rc < 0) ? BLE_ERR_MEM_CAPACITY : 0; +} + +int +ble_transport_to_hs_acl_impl(struct os_mbuf *om) +{ + return icbmsg_ble_hci_acl_tx(om); +} + +void +ble_transport_hs_init(void) +{ + hci_ipc_init(NULL, &g_hci_ipc_sm); + icbmsg_ble_hci_init(); +} +#endif /* BLE_CONTROLLER */ + +#if !MYNEWT_VAL(BLE_CONTROLLER) +int +ble_transport_to_ll_cmd_impl(void *ev_buf) +{ + int rc; + uint8_t *cmd = ev_buf; + struct ipc_icmsg_buf buf; + uint16_t length; + uint8_t type = HCI_IPC_TYPE_CMD; + + /* struct ble_hci_cmd */ + length = 3 + cmd[2]; + + rc = ipc_icbmsg_alloc_tx_buf(BLE_HCI_IPC_ID, &buf, 1 + length); + assert(rc == 0); + if (rc != 0) { + return BLE_ERR_MEM_CAPACITY; + } + + buf.data[0] = type; + buf.len = 1; + + memcpy(buf.data + buf.len, cmd, length); + buf.len += length; + + rc = ipc_icbmsg_send_buf(BLE_HCI_IPC_ID, hci_ept_local_addr, &buf); + + ble_transport_ipc_free(ev_buf); + + return (rc < 0) ? BLE_ERR_MEM_CAPACITY : 0; +} + +int +ble_transport_to_ll_acl_impl(struct os_mbuf *om) +{ + return icbmsg_ble_hci_acl_tx(om); +} + +int +ble_transport_to_ll_iso_impl(struct os_mbuf *om) +{ + return icbmsg_ble_hci_iso_tx(om); +} + +void +ble_transport_ll_init(void) +{ + hci_ipc_init(NULL, &g_hci_ipc_sm); + icbmsg_ble_hci_init(); +} +#endif /* !BLE_CONTROLLER */ diff --git a/nimble/transport/ipc/icbmsg/syscfg.yml b/nimble/transport/ipc/icbmsg/syscfg.yml new file mode 100644 index 0000000000..71e666b7d0 --- /dev/null +++ b/nimble/transport/ipc/icbmsg/syscfg.yml @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/nimble/transport/ipc/pkg.yml b/nimble/transport/ipc/pkg.yml new file mode 100644 index 0000000000..8f4f3debe0 --- /dev/null +++ b/nimble/transport/ipc/pkg.yml @@ -0,0 +1,37 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: nimble/transport/ipc +pkg.description: HCI transport via IPC +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - ipc + +pkg.deps: + - nimble + - nimble/transport/common/hci_ipc + - nimble/transport/ipc/icbmsg + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/hw/drivers/ipc" + +pkg.apis: + - ble_transport diff --git a/nimble/transport/ipc/syscfg.yml b/nimble/transport/ipc/syscfg.yml new file mode 100644 index 0000000000..cf8d31f45e --- /dev/null +++ b/nimble/transport/ipc/syscfg.yml @@ -0,0 +1,36 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +syscfg.defs: + BLE_TRANSPORT_IPC_BACKEND: + description: > + Select IPC backend. + choices: + - icbmsg + value: icbmsg + + BLE_TRANSPORT_IPC_TX_CHANNEL: + description: > + Select IPC channel to be used for TX. + range: 0..31 + value: 0 + + BLE_TRANSPORT_IPC_RX_CHANNEL: + description: > + Select IPC channel to be used for RX. + range: 0..31 + value: 1 diff --git a/nimble/transport/pkg.yml b/nimble/transport/pkg.yml index 30237f75bd..534f616431 100644 --- a/nimble/transport/pkg.yml +++ b/nimble/transport/pkg.yml @@ -38,6 +38,8 @@ pkg.deps.'BLE_TRANSPORT_HS == "dialog_cmac" || BLE_TRANSPORT_LL == "dialog_cmac" - nimble/transport/dialog_cmac pkg.deps.'BLE_TRANSPORT_HS == "nrf5340" || BLE_TRANSPORT_LL == "nrf5340"': - nimble/transport/nrf5340 +pkg.deps.'BLE_TRANSPORT_HS == "ipc" || BLE_TRANSPORT_LL == "ipc"': + - nimble/transport/ipc pkg.deps.'BLE_TRANSPORT_LL == "socket"': - nimble/transport/socket pkg.deps.'BLE_TRANSPORT_HS == "uart"': diff --git a/nimble/transport/syscfg.yml b/nimble/transport/syscfg.yml index c2b9ec1e91..4f37a2654a 100644 --- a/nimble/transport/syscfg.yml +++ b/nimble/transport/syscfg.yml @@ -29,6 +29,7 @@ syscfg.defs: - native - dialog_cmac - nrf5340 + - ipc - uart - usb - cdc @@ -44,6 +45,7 @@ syscfg.defs: - emspi - dialog_cmac - nrf5340 + - ipc - socket - apollo3 - uart_ll From 9de025c4a754e4b293ad2f1f910f065b6c837b34 Mon Sep 17 00:00:00 2001 From: Mariusz Skamra Date: Thu, 17 Jul 2025 09:04:24 +0200 Subject: [PATCH 4/6] nimble/ll: Fix NULL pointer dereference in ble_ll_sched_rmv_elem_type This fixes possible NULL pointer dereference in ble_ll_sched_rmv_elem_type that could happen if 'g_ble_ll_sched_q' queue is empty. Uninitialized 'first_removed' variable has been fixed as well. --- nimble/controller/src/ble_ll_sched.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nimble/controller/src/ble_ll_sched.c b/nimble/controller/src/ble_ll_sched.c index 7f1ac96054..c2ec46a434 100644 --- a/nimble/controller/src/ble_ll_sched.c +++ b/nimble/controller/src/ble_ll_sched.c @@ -930,10 +930,13 @@ ble_ll_sched_rmv_elem_type(uint8_t type, sched_remove_cb_func remove_cb) OS_ENTER_CRITICAL(sr); first = TAILQ_FIRST(&g_ble_ll_sched_q); - if (first->sched_type == type) { - first_removed = 1; + if (!first) { + OS_EXIT_CRITICAL(sr); + return; } + first_removed = first->sched_type == type; + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { if (entry->sched_type != type) { continue; From 5220e7e359ee90e5744a3c04f69e747ec50ec94b Mon Sep 17 00:00:00 2001 From: Mariusz Skamra Date: Wed, 23 Jul 2025 09:12:54 +0200 Subject: [PATCH 5/6] nimble/ll: Fix race condition in aux scan This fixes race condition in aux scan what was observed on Harmony. During extended scan, the HCI Reset called, that stops the active scan and clears the scan state machine. The scan stop itself, if scan response is pending, and calls ble_ll_scan_aux_sched_remove that pokes the active scan backup (from LL via event queue). The problem is that the SM is cleared already when backoff is called, which causes an assert in ble_ll_scan_req_backoff function. This change ensures the ble_ll_scan_req_backoff is called from ble_ll_reset() function context. ble_ll_reset is always called from LL task context. --- nimble/controller/src/ble_ll_scan_aux.c | 36 ++++++++++++++++++++----- nimble/controller/src/ble_ll_sched.c | 2 +- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/nimble/controller/src/ble_ll_scan_aux.c b/nimble/controller/src/ble_ll_scan_aux.c index 2071c62198..13bc23902e 100644 --- a/nimble/controller/src/ble_ll_scan_aux.c +++ b/nimble/controller/src/ble_ll_scan_aux.c @@ -682,10 +682,8 @@ ble_ll_hci_ev_send_ext_adv_report_for_ext(struct os_mbuf *rxpdu, } static void -ble_ll_scan_aux_break_ev(struct ble_npl_event *ev) +scan_aux_break(struct ble_ll_scan_aux_data *aux) { - struct ble_ll_scan_aux_data *aux = ble_npl_event_get_arg(ev); - BLE_LL_ASSERT(aux); if (ble_ll_scan_aux_need_truncation(aux)) { @@ -701,6 +699,21 @@ ble_ll_scan_aux_break_ev(struct ble_npl_event *ev) ble_ll_scan_chk_resume(); } +static void +scan_aux_break_ev(struct ble_npl_event *ev) +{ + struct ble_ll_scan_aux_data *aux = ble_npl_event_get_arg(ev); + + scan_aux_break(aux); +} + +static void +scan_aux_break_to_ll(struct ble_ll_scan_aux_data *aux) +{ + ble_npl_event_init(&aux->break_ev, scan_aux_break_ev, aux); + ble_ll_event_add(&aux->break_ev); +} + void ble_ll_scan_aux_break(struct ble_ll_scan_aux_data *aux) { @@ -710,8 +723,7 @@ ble_ll_scan_aux_break(struct ble_ll_scan_aux_data *aux) } #endif - ble_npl_event_init(&aux->break_ev, ble_ll_scan_aux_break_ev, aux); - ble_ll_event_add(&aux->break_ev); + scan_aux_break_to_ll(aux); } static int @@ -1766,7 +1778,19 @@ ble_ll_scan_aux_halt(void) void ble_ll_scan_aux_sched_remove(struct ble_ll_sched_item *sch) { - ble_ll_scan_aux_break(sch->cb_arg); + struct ble_ll_scan_aux_data *aux = sch->cb_arg; + +#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) + if (aux->flags & BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP) { + ble_ll_conn_send_connect_req_cancel(); + } +#endif + + if (ble_npl_event_is_queued(&aux->break_ev)) { + ble_ll_event_remove(&aux->break_ev); + } + + scan_aux_break(aux); } void diff --git a/nimble/controller/src/ble_ll_sched.c b/nimble/controller/src/ble_ll_sched.c index c2ec46a434..22d55a400c 100644 --- a/nimble/controller/src/ble_ll_sched.c +++ b/nimble/controller/src/ble_ll_sched.c @@ -942,8 +942,8 @@ ble_ll_sched_rmv_elem_type(uint8_t type, sched_remove_cb_func remove_cb) continue; } TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link); - remove_cb(entry); entry->enqueued = 0; + remove_cb(entry); } if (first_removed) { From cc04646aa0f3036b1cf5de1f5a18806df8651cff Mon Sep 17 00:00:00 2001 From: Mariusz Skamra Date: Fri, 18 Jul 2025 14:55:52 +0200 Subject: [PATCH 6/6] nimble/controller: Add Synchronized Receiver role support This adds Synchronized Receiver role support. The role enables reception of broadcast isochronous streams. Co-authored-by: Andrzej Kaczmarek --- nimble/controller/include/controller/ble_ll.h | 55 +- .../include/controller/ble_ll_iso.h | 76 +- .../include/controller/ble_ll_iso_big_sync.h | 51 + .../include/controller/ble_ll_isoal.h | 111 +- .../include/controller/ble_ll_sched.h | 5 + .../include/controller/ble_ll_sync.h | 9 + .../controller/include/controller/ble_phy.h | 17 + nimble/controller/src/ble_ll.c | 42 +- nimble/controller/src/ble_ll_hci.c | 65 +- nimble/controller/src/ble_ll_hci_supp_cmd.c | 4 + nimble/controller/src/ble_ll_iso.c | 685 ++++- nimble/controller/src/ble_ll_iso_big.c | 346 +-- nimble/controller/src/ble_ll_iso_big_priv.h | 160 ++ nimble/controller/src/ble_ll_iso_big_sync.c | 1437 ++++++++++ nimble/controller/src/ble_ll_iso_priv.h | 164 ++ nimble/controller/src/ble_ll_isoal.c | 640 ++++- nimble/controller/src/ble_ll_scan.c | 5 + nimble/controller/src/ble_ll_sched.c | 44 +- nimble/controller/src/ble_ll_sync.c | 139 +- nimble/controller/syscfg.yml | 26 +- nimble/controller/test/src/ble_ll_iso.c | 199 +- nimble/controller/test/src/ble_ll_isoal.c | 2456 +++++++++++++++-- nimble/include/nimble/ble.h | 44 +- nimble/include/nimble/hci_common.h | 7 +- .../transport/ipc/icbmsg/src/icbmsg_ble_hci.c | 8 +- .../transport/nrf5340/src/nrf5340_ble_hci.c | 8 +- nimble/transport/uart/src/hci_uart.c | 1 + nimble/transport/usb/src/ble_hci_usb.c | 8 + 28 files changed, 5969 insertions(+), 843 deletions(-) create mode 100644 nimble/controller/include/controller/ble_ll_iso_big_sync.h create mode 100644 nimble/controller/src/ble_ll_iso_big_priv.h create mode 100644 nimble/controller/src/ble_ll_iso_big_sync.c create mode 100644 nimble/controller/src/ble_ll_iso_priv.h diff --git a/nimble/controller/include/controller/ble_ll.h b/nimble/controller/include/controller/ble_ll.h index 9fa32d6545..7f9e548d57 100644 --- a/nimble/controller/include/controller/ble_ll.h +++ b/nimble/controller/include/controller/ble_ll.h @@ -243,6 +243,9 @@ extern STATS_SECT_DECL(ble_ll_stats) ble_ll_stats; #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) #define BLE_LL_STATE_BIG (9) #endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) +#define BLE_LL_STATE_BIG_SYNC (10) +#endif /* LL Features */ #define BLE_LL_FEAT_LE_ENCRYPTION (0x0000000000001) @@ -300,8 +303,8 @@ extern STATS_SECT_DECL(ble_ll_stats) ble_ll_stats; #define BLE_LL_CONN_CLEAR_FEATURE(connsm, feature) (connsm->conn_features &= ~(feature)) /* All the features which can be controlled by the Host */ -#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ENHANCED_CONN_UPDATE) | \ - MYNEWT_VAL(BLE_LL_ADV_CODING_SELECTION) +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ENHANCED_CONN_UPDATE) | \ + MYNEWT_VAL(BLE_LL_ADV_CODING_SELECTION) | MYNEWT_VAL(BLE_LL_ISO) #define BLE_LL_HOST_CONTROLLED_FEATURES (1) #else #define BLE_LL_HOST_CONTROLLED_FEATURES (0) @@ -462,6 +465,54 @@ struct ble_dev_addr ((((hdr) & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_DATA_START) || \ (((hdr) & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_DATA_FRAG)) +/* + * Broadcast Isochronous Data Channel format + * + * -> Header (2 bytes) + * -> LSB contains llid, cssn and cstf + * -> MSB contains length (8 bits) + * -> Payload (0 to 251) + * -> MIC (0 or 4 bytes) + */ +#define BLE_LL_BIS_PDU_HDR_LLID_MASK (0x03) +#define BLE_LL_BIS_PDU_HDR_CSSN_MASK (0x1C) +#define BLE_LL_BIS_PDU_HDR_CSTF_MASK (0x20) +#define BLE_LL_BIS_PDU_HDR_RFU_MASK (0xC0) +#define BLE_LL_ISO_DATA_PAYLOAD_MAX (251) +#define BLE_LL_ISO_DATA_MIC_LEN (4) + +/* Broadcast Isochronous PDU header LLID definitions */ +#define BLE_LL_BIS_LLID_DATA_PDU_UNFRAMED_CMPLT (0b00) +#define BLE_LL_BIS_LLID_DATA_PDU_UNFRAMED_SC (0b01) +#define BLE_LL_BIS_LLID_DATA_PDU_FRAMED (0b10) +#define BLE_LL_BIS_LLID_CTRL_PDU (0b11) + +#define BLE_LL_BIS_PDU_HDR_LLID(hdr) \ + (((hdr) & BLE_LL_BIS_PDU_HDR_LLID_MASK) >> 0) +#define BLE_LL_BIS_PDU_HDR_CSSN(hdr) \ + (((hdr) & BLE_LL_BIS_PDU_HDR_CSSN_MASK) >> 2) +#define BLE_LL_BIS_PDU_HDR_CSTF(hdr) \ + (((hdr) & BLE_LL_BIS_PDU_HDR_CSTF_MASK) >> 5) + +#define BLE_LL_BIS_LLID_IS_CTRL(hdr) \ + (BLE_LL_BIS_PDU_HDR_LLID(hdr) == BLE_LL_BIS_LLID_CTRL_PDU) +#define BLE_LL_BIS_LLID_IS_DATA(hdr) \ + ((BLE_LL_BIS_PDU_HDR_LLID(hdr) == BLE_LL_BIS_LLID_DATA_PDU_UNFRAMED_CMPLT) || \ + (BLE_LL_BIS_PDU_HDR_LLID(hdr) == BLE_LL_BIS_LLID_DATA_PDU_UNFRAMED_SC) || \ + (BLE_LL_BIS_PDU_HDR_LLID(hdr) == BLE_LL_BIS_LLID_DATA_PDU_FRAMED)) + +#define BLE_LL_BIG_CTRL_CHAN_MAP_IND (0x00) +struct ble_ll_big_ctrl_chan_map_ind { + uint8_t chan_map[BLE_LL_CHAN_MAP_LEN]; + uint16_t instant; +} __attribute__((packed)); + +#define BLE_LL_BIG_CTRL_TERM_IND (0x01) +struct ble_ll_big_ctrl_term_ind { + uint8_t reason; + uint16_t instant; +} __attribute__((packed)); + /* * CONNECT_REQ * -> InitA (6 bytes) diff --git a/nimble/controller/include/controller/ble_ll_iso.h b/nimble/controller/include/controller/ble_ll_iso.h index 8d6cbde562..62aca78407 100644 --- a/nimble/controller/include/controller/ble_ll_iso.h +++ b/nimble/controller/include/controller/ble_ll_iso.h @@ -17,52 +17,16 @@ * under the License. */ -#ifndef H_BLE_LL_ISO -#define H_BLE_LL_ISO +#ifndef H_BLE_LL_ISO_ +#define H_BLE_LL_ISO_ #include -#include +#include #ifdef __cplusplus extern "C" { #endif -struct ble_ll_iso_data_path { - uint8_t data_path_id; - uint8_t enabled : 1; -}; -struct ble_ll_iso_test_mode { - struct { - uint32_t rand; - uint8_t payload_type; - uint8_t enabled : 1; - } transmit; -}; -struct ble_ll_iso_conn { - /* Connection handle */ - uint16_t handle; - - /* Maximum SDU size */ - uint16_t max_sdu; - - /* ISO Data Path */ - struct ble_ll_iso_data_path data_path; - - /* ISO Test Mode */ - struct ble_ll_iso_test_mode test_mode; - - /* ISOAL Multiplexer */ - struct ble_ll_isoal_mux mux; - - /* HCI SDU Fragment */ - struct os_mbuf *frag; - - /* Number of Completed Packets */ - uint16_t num_completed_pkt; - - STAILQ_ENTRY(ble_ll_iso_conn) iso_conn_q_next; -}; - /* HCI command handlers */ int ble_ll_iso_read_tx_sync(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, uint8_t *rsplen); int ble_ll_iso_set_cig_param(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, uint8_t *rsplen); @@ -74,41 +38,23 @@ int ble_ll_iso_reject_cis_req(const uint8_t *cmdbuf, uint8_t len); int ble_ll_iso_create_big(const uint8_t *cmdbuf, uint8_t len); int ble_ll_iso_create_big_test(const uint8_t *cmdbuf, uint8_t len); int ble_ll_iso_terminate_big(const uint8_t *cmdbuf, uint8_t len); -int ble_ll_iso_big_create_sync(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_iso_big_create_sync(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); int ble_ll_iso_big_terminate_sync(const uint8_t *cmdbuf, uint8_t len); int ble_ll_iso_setup_iso_data_path(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, uint8_t *rsplen); int ble_ll_iso_remove_iso_data_path(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, uint8_t *rsplen); int ble_ll_iso_transmit_test(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, uint8_t *rsplen); -int ble_ll_iso_receive_test(const uint8_t *cmdbuf, uint8_t len); -int ble_ll_iso_read_counters_test(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_iso_receive_test(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_iso_read_counters_test(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); int ble_ll_iso_end_test(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, uint8_t *rsplen); void ble_ll_iso_init(void); void ble_ll_iso_reset(void); -/* ISO Data handler */ -int ble_ll_iso_data_in(struct os_mbuf *om); - -int ble_ll_iso_pdu_get(struct ble_ll_iso_conn *conn, uint8_t idx, uint32_t pkt_counter, uint8_t *llid, void *dptr); - -struct ble_ll_iso_conn_init_param { - uint32_t iso_interval_us; - uint32_t sdu_interval_us; - uint16_t conn_handle; - uint16_t max_sdu; - uint8_t max_pdu; - uint8_t framing; - uint8_t pte; - uint8_t bn; -}; - -void ble_ll_iso_conn_init(struct ble_ll_iso_conn *conn, struct ble_ll_iso_conn_init_param *param); -void ble_ll_iso_conn_free(struct ble_ll_iso_conn *conn); - -int ble_ll_iso_conn_event_start(struct ble_ll_iso_conn *conn, uint32_t timestamp); -int ble_ll_iso_conn_event_done(struct ble_ll_iso_conn *conn); - -struct ble_ll_iso_conn *ble_ll_iso_conn_find_by_handle(uint16_t conn_handle); +/* HCI ISO Data SDU handler */ +int ble_ll_hci_iso_data_in(struct os_mbuf *om); #ifdef __cplusplus } diff --git a/nimble/controller/include/controller/ble_ll_iso_big_sync.h b/nimble/controller/include/controller/ble_ll_iso_big_sync.h new file mode 100644 index 0000000000..f06c1d1276 --- /dev/null +++ b/nimble/controller/include/controller/ble_ll_iso_big_sync.h @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_ISO_BIG_SYNC_ +#define H_BLE_LL_ISO_BIG_SYNC_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + +int ble_ll_iso_big_sync_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *rxhdr); +int ble_ll_iso_big_sync_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr); +int ble_ll_iso_big_sync_rx_isr_early_end(const uint8_t *rxbuf, + const struct ble_mbuf_hdr *rxhdr); +void ble_ll_iso_big_sync_rx_pdu_in(struct os_mbuf **rxpdu, struct ble_mbuf_hdr *hdr); + +void ble_ll_iso_big_sync_wfr_timer_exp(void); +void ble_ll_iso_big_sync_halt(void); + +int ble_ll_iso_big_sync_hci_create(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_iso_big_sync_hci_terminate(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); + +void ble_ll_iso_big_sync_init(void); +void ble_ll_iso_big_sync_reset(void); + +#endif /* BLE_LL_ISO_BROADCAST_SYNC */ + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_ISO_BIG_SYNC_ */ diff --git a/nimble/controller/include/controller/ble_ll_isoal.h b/nimble/controller/include/controller/ble_ll_isoal.h index a590a0f6e7..b9aeb006cd 100644 --- a/nimble/controller/include/controller/ble_ll_isoal.h +++ b/nimble/controller/include/controller/ble_ll_isoal.h @@ -21,31 +21,53 @@ #define H_BLE_LL_ISOAL_ #include -#include #ifdef __cplusplus extern "C" { #endif +#define BLE_LL_ISOAL_SEGHDR(sc, cmplt, len) \ + ((uint16_t)((sc) & 0x01) | (((cmplt) & 0x01) << 1) | ((len) & 0xff) << 8) + +#define BLE_LL_ISOAL_SEGHDR_SC(word) ((word) & 0x01) +#define BLE_LL_ISOAL_SEGHDR_CMPLT(word) ((word >> 1) & 0x01) +#define BLE_LL_ISOAL_SEGHDR_LEN(word) ((word >> 8) & 0xff) + +#define BLE_LL_ISOAL_TIME_OFFSET_LEN 3 + +struct ble_ll_isoal_config { + /* ISO Interval */ + uint32_t iso_interval_us; + /* SDU Interval */ + uint32_t sdu_interval_us; + /* Maximum SDU size */ + uint16_t max_sdu; + /* Max PDU length */ + uint8_t max_pdu; + /* Burst Number */ + uint8_t bn; + /**/ + uint8_t pte; + /* Framed */ + uint8_t framed : 1; + /* Framing mode = segmentable/unsegmented */ + uint8_t unsegmented : 1; +}; + struct ble_ll_isoal_mux { + struct ble_ll_isoal_config config; #if MYNEWT_VAL(BLE_LL_ISOAL_MUX_PREFILL) uint8_t active; #endif - - /* Max PDU length */ - uint8_t max_pdu; /* Number of expected SDUs per ISO interval */ uint8_t sdu_per_interval; /* Number of expected PDUs per SDU */ uint8_t pdu_per_sdu; /* Number of SDUs required to fill complete BIG/CIG event (i.e. with pt) */ uint8_t sdu_per_event; - /* Number of SDUs available for current event */ + /* Number of SDUs available for the current event */ uint8_t sdu_in_event; - /* Burst Number */ - uint8_t bn; - STAILQ_HEAD(, os_mbuf_pkthdr) sdu_q; uint16_t sdu_q_len; @@ -57,39 +79,64 @@ struct ble_ll_isoal_mux { /* The head SDU Segment is the Continuation of an SDU */ uint8_t sc : 1; - uint8_t framed : 1; - uint8_t framing_mode : 1; }; -#define BLE_LL_ISOAL_SEGHDR(sc, cmplt, len) \ - ((uint16_t)((sc) & 0x01) | (((cmplt) & 0x01) << 1) | ((len) & 0xff) << 8) +void ble_ll_isoal_mux_init(struct ble_ll_isoal_mux *mux, + struct ble_ll_isoal_config *config); +void ble_ll_isoal_mux_reset(struct ble_ll_isoal_mux *mux); +int ble_ll_isoal_mux_event_start(struct ble_ll_isoal_mux *mux, uint32_t timestamp); +int ble_ll_isoal_mux_event_done(struct ble_ll_isoal_mux *mux); +int ble_ll_isoal_mux_pdu_get(struct ble_ll_isoal_mux *mux, uint8_t idx, + uint8_t *llid, void *dptr); +void ble_ll_isoal_mux_sdu_put(struct ble_ll_isoal_mux *mux, struct os_mbuf *om); -#define BLE_LL_ISOAL_SEGHDR_SC(word) ((word) & 0x01) -#define BLE_LL_ISOAL_SEGHDR_CMPLT(word) ((word >> 1) & 0x01) -#define BLE_LL_ISOAL_SEGHDR_LEN(word) ((word >> 8) & 0xff) +/* Forward declaration */ +struct ble_ll_isoal_demux; -#define BLE_LL_ISOAL_MUX_IS_FRAMED(framing) \ - ((framing) == BLE_HCI_ISO_FRAMING_FRAMED_SEGMENTABLE || \ - (framing) == BLE_HCI_ISO_FRAMING_FRAMED_UNSEGMENTED) +/* ISOAL Demultiplexer callback structure */ +struct ble_ll_isoal_demux_cb { + void (*sdu_cb)(struct ble_ll_isoal_demux *demux, const struct os_mbuf *om, + uint32_t timestamp, uint16_t seq_num, bool valid); +}; -void ble_ll_isoal_mux_init(struct ble_ll_isoal_mux *mux, uint8_t max_pdu, - uint32_t iso_interval_us, uint32_t sdu_interval_us, - uint8_t bn, uint8_t pte, bool framed, - uint8_t framing_mode); -void ble_ll_isoal_mux_free(struct ble_ll_isoal_mux *mux); +struct ble_ll_isoal_demux { + struct ble_ll_isoal_config config; -int ble_ll_isoal_mux_event_start(struct ble_ll_isoal_mux *mux, - uint32_t timestamp); -int ble_ll_isoal_mux_event_done(struct ble_ll_isoal_mux *mux); + uint8_t active; -int ble_ll_isoal_mux_pdu_get(struct ble_ll_isoal_mux *mux, uint8_t idx, - uint8_t *llid, void *dptr); + /* Number of expected SDUs per ISO interval */ + uint8_t sdu_per_interval; + /* Number of expected PDUs per SDU */ + uint8_t pdu_per_sdu; + /* Number of SDUs expected for the current event */ + uint8_t sdu_in_event; + + STAILQ_HEAD(, os_mbuf_pkthdr) pdu_q; + + uint32_t sdu_counter; + + uint32_t ref_time; + uint32_t last_rx_timestamp; -void ble_ll_isoal_mux_sdu_enqueue(struct ble_ll_isoal_mux *mux, - struct os_mbuf *om); + struct os_mbuf *frag; -void ble_ll_isoal_init(void); -void ble_ll_isoal_reset(void); + const struct ble_ll_isoal_demux_cb *cb; +}; + +void ble_ll_isoal_demux_init(struct ble_ll_isoal_demux *demux, + struct ble_ll_isoal_config *config); +void ble_ll_isoal_demux_reset(struct ble_ll_isoal_demux *demux); +int ble_ll_isoal_demux_event_start(struct ble_ll_isoal_demux *demux, uint32_t timestamp); +int ble_ll_isoal_demux_event_done(struct ble_ll_isoal_demux *demux); +void ble_ll_isoal_demux_pdu_put(struct ble_ll_isoal_demux *demux, uint8_t idx, + struct os_mbuf *om); + +static inline void +ble_ll_isoal_demux_cb_set(struct ble_ll_isoal_demux *demux, + const struct ble_ll_isoal_demux_cb *cb) +{ + demux->cb = cb; +} #ifdef __cplusplus } diff --git a/nimble/controller/include/controller/ble_ll_sched.h b/nimble/controller/include/controller/ble_ll_sched.h index 5daa647fab..de893bd772 100644 --- a/nimble/controller/include/controller/ble_ll_sched.h +++ b/nimble/controller/include/controller/ble_ll_sched.h @@ -71,6 +71,7 @@ extern uint8_t g_ble_ll_sched_offset_ticks; #define BLE_LL_SCHED_TYPE_SYNC (7) #define BLE_LL_SCHED_TYPE_SCAN_AUX (8) #define BLE_LL_SCHED_TYPE_BIG (9) +#define BLE_LL_SCHED_TYPE_BIG_SYNC (10) #if MYNEWT_VAL(BLE_LL_EXT) #define BLE_LL_SCHED_TYPE_EXTERNAL (255) #endif @@ -222,6 +223,10 @@ uint32_t ble_ll_sched_css_get_conn_interval_us(void); int ble_ll_sched_iso_big(struct ble_ll_sched_item *sch, int first, int fixed); #endif /* BLE_LL_ISO_BROADCASTER */ +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) +int ble_ll_sched_iso_big_sync(struct ble_ll_sched_item *sch); +#endif /* BLE_LL_ISO_BROADCAST_SYNC */ + #ifdef __cplusplus } #endif diff --git a/nimble/controller/include/controller/ble_ll_sync.h b/nimble/controller/include/controller/ble_ll_sync.h index 5d7ef7a864..8b27c85ce6 100644 --- a/nimble/controller/include/controller/ble_ll_sync.h +++ b/nimble/controller/include/controller/ble_ll_sync.h @@ -62,6 +62,15 @@ void ble_ll_sync_rmvd_from_sched(struct ble_ll_sync_sm *sm); uint32_t ble_ll_sync_get_event_end_time(void); +typedef void (*ble_ll_sync_biginfo_cb_t)(struct ble_ll_sync_sm *syncsm, + uint8_t sca, uint32_t sync_ticks, + uint8_t sync_rem_us, const uint8_t *data, + uint8_t len, void *arg); + +struct ble_ll_sync_sm *ble_ll_sync_get(uint8_t handle); +void ble_ll_sync_biginfo_cb_set(struct ble_ll_sync_sm *syncsm, + ble_ll_sync_biginfo_cb_t cb, void *cb_arg); + bool ble_ll_sync_enabled(void); void ble_ll_sync_reset(void); diff --git a/nimble/controller/include/controller/ble_phy.h b/nimble/controller/include/controller/ble_phy.h index ad7a6974d3..ea00a7753f 100644 --- a/nimble/controller/include/controller/ble_phy.h +++ b/nimble/controller/include/controller/ble_phy.h @@ -230,6 +230,23 @@ static inline int ble_ll_phy_to_phy_mode(int phy, int phy_options) return phy_mode; } +static inline bool +ble_ll_phy_is_supported(uint8_t phy) +{ + switch (phy) { + case BLE_PHY_1M: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case BLE_PHY_2M: +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_PHY_CODED: +#endif + return true; + default: + return false; + } +} + #if MYNEWT_VAL(BLE_LL_DTM) void ble_phy_enable_dtm(void); void ble_phy_disable_dtm(void); diff --git a/nimble/controller/src/ble_ll.c b/nimble/controller/src/ble_ll.c index 24a9849d90..2c89854d06 100644 --- a/nimble/controller/src/ble_ll.c +++ b/nimble/controller/src/ble_ll.c @@ -52,6 +52,7 @@ #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) #include "controller/ble_ll_iso_big.h" #endif +#include "controller/ble_ll_iso_big_sync.h" #if MYNEWT_VAL(BLE_LL_EXT) #include "controller/ble_ll_ext.h" #endif @@ -93,6 +94,9 @@ static const uint64_t g_ble_ll_host_controlled_features = #endif #if MYNEWT_VAL(BLE_LL_ADV_CODING_SELECTION) BLE_LL_FEAT_ADV_CODING_SEL_HOST | +#endif +#if MYNEWT_VAL(BLE_LL_ISO) + BLE_LL_FEAT_CIS_HOST | #endif 0; #endif @@ -814,6 +818,11 @@ ble_ll_wfr_timer_exp(void *arg) case BLE_LL_STATE_EXTERNAL: ble_ll_ext_wfr_timer_exp(); break; +#endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + case BLE_LL_STATE_BIG_SYNC: + ble_ll_iso_big_sync_wfr_timer_exp(); + break; #endif default: break; @@ -994,6 +1003,11 @@ ble_ll_rx_pkt_in(void) case BLE_LL_STATE_EXTERNAL: ble_ll_ext_rx_pkt_in(m, ble_hdr); break; +#endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + case BLE_LL_STATE_BIG_SYNC: + ble_ll_iso_big_sync_rx_pdu_in(&m, ble_hdr); + break; #endif default: /* Any other state should never occur */ @@ -1145,6 +1159,11 @@ ble_ll_rx_start(uint8_t *rxbuf, uint8_t chan, struct ble_mbuf_hdr *rxhdr) case BLE_LL_STATE_EXTERNAL: rc = ble_ll_ext_rx_isr_start(pdu_type, rxhdr); break; +#endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + case BLE_LL_STATE_BIG_SYNC: + rc = ble_ll_iso_big_sync_rx_isr_start(pdu_type, rxhdr); + break; #endif default: /* Should not be in this state! */ @@ -1189,6 +1208,13 @@ ble_ll_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) ble_ll_trace_u32x3(BLE_LL_TRACE_ID_RX_END, pdu_type, len, rxhdr->rxinfo.flags); +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_BIG_SYNC) { + rc = ble_ll_iso_big_sync_rx_isr_end(rxbuf, rxhdr); + return rc; + } +#endif + #if MYNEWT_VAL(BLE_LL_EXT) if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_EXTERNAL) { rc = ble_ll_ext_rx_isr_end(rxbuf, rxhdr); @@ -1324,7 +1350,12 @@ ble_ll_rx_early_end(const uint8_t *rxbuf, const struct ble_mbuf_hdr *rxhdr) { int rc = 0; - /* TODO */ +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_BIG_SYNC) { + rc = ble_ll_iso_big_sync_rx_isr_early_end(rxbuf, rxhdr); + return rc; + } +#endif return rc; } @@ -1723,6 +1754,9 @@ ble_ll_reset(void) #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) ble_ll_iso_big_reset(); #endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + ble_ll_iso_big_sync_reset(); +#endif #if MYNEWT_VAL(BLE_LL_ISO) ble_ll_iso_reset(); @@ -1968,6 +2002,9 @@ ble_ll_init(void) #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) features |= BLE_LL_FEAT_ISO_BROADCASTER; #endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + features |= BLE_LL_FEAT_SYNC_RECV; +#endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ENHANCED_CONN_UPDATE) features |= BLE_LL_FEAT_CONN_SUBRATING; @@ -2013,6 +2050,9 @@ ble_ll_init(void) #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) ble_ll_iso_big_init(); #endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + ble_ll_iso_big_sync_init(); +#endif #if MYNEWT_VAL(BLE_LL_EXT) ble_ll_ext_init(); diff --git a/nimble/controller/src/ble_ll_hci.c b/nimble/controller/src/ble_ll_hci.c index 07b0aec1c5..19f1a796a1 100644 --- a/nimble/controller/src/ble_ll_hci.c +++ b/nimble/controller/src/ble_ll_hci.c @@ -16,31 +16,32 @@ * specific language governing permissions and limitations * under the License. */ -#include -#include -#include -#include "syscfg/syscfg.h" -#include "os/os.h" -#include "nimble/ble.h" -#include "nimble/nimble_opt.h" -#include "nimble/hci_common.h" -#include "controller/ble_ll_utils.h" +#include "controller/ble_ll_hci.h" +#include "ble_ll_conn_priv.h" +#include "ble_ll_hci_priv.h" +#include "ble_ll_priv.h" #include "controller/ble_hw.h" -#include "controller/ble_ll_adv.h" -#include "controller/ble_ll_scan.h" #include "controller/ble_ll.h" -#include "controller/ble_ll_hci.h" -#include "controller/ble_ll_whitelist.h" +#include "controller/ble_ll_adv.h" +#include "controller/ble_ll_cs.h" +#include "controller/ble_ll_iso.h" +#include "controller/ble_ll_iso_big.h" +#include "controller/ble_ll_iso_big_sync.h" +#include "controller/ble_ll_isoal.h" #include "controller/ble_ll_resolv.h" +#include "controller/ble_ll_scan.h" #include "controller/ble_ll_sync.h" +#include "controller/ble_ll_utils.h" +#include "controller/ble_ll_whitelist.h" +#include "nimble/ble.h" +#include "nimble/hci_common.h" +#include "nimble/nimble_opt.h" +#include "os/os.h" +#include "syscfg/syscfg.h" +#include #include -#include "controller/ble_ll_isoal.h" -#include "controller/ble_ll_iso.h" -#include "controller/ble_ll_iso_big.h" -#include "controller/ble_ll_cs.h" -#include "ble_ll_priv.h" -#include "ble_ll_conn_priv.h" -#include "ble_ll_hci_priv.h" +#include +#include #if MYNEWT_VAL(BLE_LL_DTM) #include "ble_ll_dtm_priv.h" @@ -675,6 +676,9 @@ ble_ll_hci_le_cmd_send_cmd_status(uint16_t ocf) case BLE_HCI_OCF_LE_CREATE_BIG_TEST: case BLE_HCI_OCF_LE_TERMINATE_BIG: #endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + case BLE_HCI_OCF_LE_BIG_CREATE_SYNC: +#endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_SCA_UPDATE) case BLE_HCI_OCF_LE_REQ_PEER_SCA: #endif @@ -1288,6 +1292,25 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf, rc = ble_ll_iso_transmit_test(cmdbuf, len, rspbuf, rsplen); break; #endif /* BLE_LL_ISO_BROADCASTER */ +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + case BLE_HCI_OCF_LE_ISO_RECEIVE_TEST: + rc = ble_ll_iso_receive_test(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_ISO_READ_TEST_COUNTERS: + rc = ble_ll_iso_read_counters_test(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_BIG_CREATE_SYNC: + rc = ble_ll_iso_big_sync_hci_create(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_BIG_TERMINATE_SYNC: + rc = ble_ll_iso_big_sync_hci_terminate(cmdbuf, len, rspbuf, rsplen); + break; +#if !MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) + case BLE_HCI_OCF_LE_TERMINATE_BIG: + rc = BLE_ERR_CMD_DISALLOWED; + break; +#endif +#endif /* BLE_LL_ISO_BROADCAST_SYNC */ #if MYNEWT_VAL(BLE_LL_ISO) case BLE_HCI_OCF_LE_SETUP_ISO_DATA_PATH: rc = ble_ll_iso_setup_iso_data_path(cmdbuf, len, rspbuf, rsplen); @@ -2001,7 +2024,7 @@ int ble_ll_hci_iso_rx(struct os_mbuf *om) { #if MYNEWT_VAL(BLE_LL_ISO) - ble_ll_iso_data_in(om); + ble_ll_hci_iso_data_in(om); #else os_mbuf_free_chain(om); #endif diff --git a/nimble/controller/src/ble_ll_hci_supp_cmd.c b/nimble/controller/src/ble_ll_hci_supp_cmd.c index 31edf1c2aa..6a0fbd8600 100644 --- a/nimble/controller/src/ble_ll_hci_supp_cmd.c +++ b/nimble/controller/src/ble_ll_hci_supp_cmd.c @@ -281,6 +281,10 @@ static const uint8_t octet_42 = OCTET( ); static const uint8_t octet_43 = OCTET( +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + BIT(0) /* LE BIG Create Sync */ + BIT(1) /* LE BIG Terminate Sync */ +#endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_SCA_UPDATE) BIT(2) /* HCI LE Request Peer SCA */ #endif diff --git a/nimble/controller/src/ble_ll_iso.c b/nimble/controller/src/ble_ll_iso.c index 03a31d5fab..c00462ee0c 100644 --- a/nimble/controller/src/ble_ll_iso.c +++ b/nimble/controller/src/ble_ll_iso.c @@ -17,28 +17,229 @@ * under the License. */ -#include -#include -#include +#include "ble_ll_iso_priv.h" #include -#include #include +#include #include +#include +#include +#include +#include #if MYNEWT_VAL(BLE_LL_ISO) +#define HCI_ISO_PKTHDR_OVERHEAD (sizeof(struct os_mbuf_pkthdr)) +#define HCI_ISO_BUF_OVERHEAD (sizeof(struct os_mbuf) + HCI_ISO_PKTHDR_OVERHEAD) + +/* Core 5.4 | Vol 4, Part E, 4.1.1 + * The ISO_Data_Packet_Length parameter of this command specifies + * the maximum buffer size for each HCI ISO Data packet (excluding the header + * but including optional fields such as ISO_SDU_Length). + */ +#define HCI_ISO_DATA_PKT_LEN MYNEWT_VAL(BLE_TRANSPORT_ISO_SIZE) +#define HCI_ISO_DATA_BUF_SIZE \ + OS_ALIGN(HCI_ISO_DATA_PKT_LEN + sizeof(struct ble_hci_iso), 4) + +#define HCI_ISO_TIMESTAMP_LEN 4 +#define HCI_ISO_DATA_PKTHDR_LEN \ + (sizeof(struct ble_hci_iso) + HCI_ISO_TIMESTAMP_LEN + \ + sizeof(struct ble_hci_iso_data)) + +#define HCI_ISO_BUF_COUNT MYNEWT_VAL(BLE_TRANSPORT_ISO_FROM_HS_COUNT) +#define HCI_ISO_BUF_SIZE (HCI_ISO_DATA_BUF_SIZE + HCI_ISO_BUF_OVERHEAD) +static os_membuf_t hci_iso_data_pkt_membuf[OS_MEMPOOL_SIZE(HCI_ISO_BUF_COUNT, HCI_ISO_BUF_SIZE)]; +static struct os_mbuf_pool hci_iso_data_pkt_mbuf_pool; +static struct os_mempool hci_iso_data_pkt_mempool; + STAILQ_HEAD(ble_ll_iso_conn_q, ble_ll_iso_conn); struct ble_ll_iso_conn_q ll_iso_conn_q; +static void +hci_iso_sdu_send(uint16_t conn_handle, const struct os_mbuf *sdu, + uint32_t timestamp, uint16_t seq_num, bool valid) +{ + struct os_mbuf *om; + struct ble_hci_iso_data *hci_iso_data; + struct ble_hci_iso *hci_iso; + uint32_t *ts; + uint16_t iso_sdu_len; + uint16_t iso_sdu_frag_len; + uint16_t iso_sdu_offset; + uint8_t pkt_status_flag; + uint8_t pb_flag; + int rc; + + om = os_mbuf_get_pkthdr(&hci_iso_data_pkt_mbuf_pool, 0); + BLE_LL_ASSERT(om); + + /* Prepare room for HCI ISO SDU packet header */ + hci_iso = os_mbuf_extend(om, sizeof(*hci_iso)); + BLE_LL_ASSERT(hci_iso); + + ts = os_mbuf_extend(om, sizeof(*ts)); + BLE_LL_ASSERT(ts); + put_le32(ts, timestamp); + + /* Prepare room for ISO SDU header */ + hci_iso_data = os_mbuf_extend(om, sizeof(*hci_iso_data)); + BLE_LL_ASSERT(hci_iso_data); + + hci_iso_data->packet_seq_num = htole16(seq_num); + if (sdu == NULL) { + iso_sdu_len = 0; + pkt_status_flag = BLE_HCI_ISO_PKT_STATUS_LOST; + } else { + iso_sdu_len = os_mbuf_len(sdu); + pkt_status_flag = valid ? BLE_HCI_ISO_PKT_STATUS_VALID + : BLE_HCI_ISO_PKT_STATUS_INVALID; + } + hci_iso_data->sdu_len = + htole16(BLE_HCI_ISO_SDU_LENGTH_DEFINE(iso_sdu_len, pkt_status_flag)); + + iso_sdu_frag_len = min(OS_MBUF_TRAILINGSPACE(om), iso_sdu_len); + if (iso_sdu_frag_len > 0) { + os_mbuf_appendfrom(om, sdu, 0, iso_sdu_frag_len); + } + iso_sdu_offset = iso_sdu_frag_len; + + pb_flag = iso_sdu_frag_len == iso_sdu_len ? BLE_HCI_ISO_PB_COMPLETE + : BLE_HCI_ISO_PB_FIRST; + hci_iso->handle = htole16(BLE_HCI_ISO_HANDLE(conn_handle, pb_flag, true)); + hci_iso->length = + htole16(HCI_ISO_TIMESTAMP_LEN + sizeof(*hci_iso_data) + iso_sdu_frag_len); + + rc = ble_transport_to_hs_iso(om); + if (rc != 0) { + os_mbuf_free_chain(om); + BLE_LL_ASSERT(0); + } + + while (iso_sdu_offset < iso_sdu_len) { + om = os_mbuf_get_pkthdr(&hci_iso_data_pkt_mbuf_pool, 0); + BLE_LL_ASSERT(om); + + /* Prepare room for HCI ISO SDU packet header */ + hci_iso = os_mbuf_extend(om, sizeof(*hci_iso)); + BLE_LL_ASSERT(hci_iso); + + iso_sdu_frag_len = + min(OS_MBUF_TRAILINGSPACE(om), iso_sdu_len - iso_sdu_offset); + if (iso_sdu_frag_len > 0) { + os_mbuf_appendfrom(om, sdu, iso_sdu_offset, iso_sdu_frag_len); + } + iso_sdu_offset += iso_sdu_frag_len; + + pb_flag = iso_sdu_offset < iso_sdu_len ? BLE_HCI_ISO_PB_CONTINUATION + : BLE_HCI_ISO_PB_LAST; + hci_iso->handle = htole16(BLE_HCI_ISO_HANDLE(conn_handle, pb_flag, false)); + hci_iso->length = htole16(iso_sdu_frag_len); + + rc = ble_transport_to_hs_iso(om); + if (rc != 0) { + os_mbuf_free_chain(om); + BLE_LL_ASSERT(0); + } + } +} + +static struct ble_ll_iso_data_path_cb hci_iso_data_path_cb = { + .sdu_out = hci_iso_sdu_send, +}; + +static void +iso_test_sdu_send(uint16_t conn_handle, const struct os_mbuf *sdu, + uint32_t timestamp, uint16_t seq_num, bool valid) +{ + struct ble_ll_iso_conn *conn; + struct ble_ll_iso_rx *rx; + + conn = ble_ll_iso_conn_find_by_handle(conn_handle); + if (conn == NULL) { + return; + } + + rx = conn->rx; + BLE_LL_ASSERT(rx); + + if (sdu == NULL) { + rx->test.missed_sdu_count++; + return; + } + + if (!valid) { + rx->test.failed_sdu_count++; + return; + } + + /* Zero size SDU expected */ + if (rx->test.payload_type == 0b00 && os_mbuf_len(sdu) != 0) { + rx->test.failed_sdu_count++; + return; + } + + /* Max size SDU expected */ + if (rx->test.payload_type == 0b10 && os_mbuf_len(sdu) != rx->params->max_sdu) { + rx->test.failed_sdu_count++; + return; + } + + rx->test.received_sdu_count++; +} + +static struct ble_ll_iso_data_path_cb test_iso_data_path_cb = { + .sdu_out = iso_test_sdu_send, +}; + +static const struct ble_ll_iso_data_path_cb * +ble_ll_iso_data_path_get(uint8_t data_path_id) +{ + if (data_path_id == BLE_HCI_ISO_DATA_PATH_ID_HCI) { + return &hci_iso_data_path_cb; + } + + /* We do not (yet) support any vendor-specific data path */ + return NULL; +} + +static void +iso_sdu_cb(struct ble_ll_isoal_demux *demux, const struct os_mbuf *om, + uint32_t timestamp, uint16_t seq_num, bool valid) +{ + struct ble_ll_iso_rx *rx; + + rx = CONTAINER_OF(demux, struct ble_ll_iso_rx, demux); + + if (rx->data_path != NULL && rx->data_path->sdu_out != NULL) { + rx->data_path->sdu_out(rx->conn->handle, om, timestamp, seq_num, valid); + } +} + +static const struct ble_ll_isoal_demux_cb isoal_demux_cb = { + .sdu_cb = iso_sdu_cb, +}; + int ble_ll_iso_setup_iso_data_path(const uint8_t *cmdbuf, uint8_t cmdlen, uint8_t *rspbuf, uint8_t *rsplen) { const struct ble_hci_le_setup_iso_data_path_cp *cmd = (const void *)cmdbuf; struct ble_hci_le_setup_iso_data_path_rp *rsp = (void *)rspbuf; + const struct ble_ll_iso_data_path_cb *data_path; struct ble_ll_iso_conn *conn; + struct ble_ll_iso_rx *rx; + struct ble_ll_iso_tx *tx; uint16_t conn_handle; + if (cmdlen < sizeof(*cmd) || cmdlen != sizeof(*cmd) + cmd->codec_config_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->data_path_dir != BLE_HCI_ISO_DATA_PATH_DIR_INPUT && + cmd->data_path_dir != BLE_HCI_ISO_DATA_PATH_DIR_OUTPUT) { + return BLE_ERR_CMD_DISALLOWED; + } + conn_handle = le16toh(cmd->conn_handle); conn = ble_ll_iso_conn_find_by_handle(conn_handle); @@ -46,31 +247,41 @@ ble_ll_iso_setup_iso_data_path(const uint8_t *cmdbuf, uint8_t cmdlen, return BLE_ERR_UNK_CONN_ID; } - if (conn->mux.bn == 0) { - return BLE_ERR_UNSUPPORTED; - } - - if (conn->data_path.enabled) { + data_path = ble_ll_iso_data_path_get(cmd->data_path_id); + if (data_path == NULL) { return BLE_ERR_CMD_DISALLOWED; } - /* Only input for now since we only support BIS */ - if (cmd->data_path_dir) { - return BLE_ERR_CMD_DISALLOWED; - } + if (cmd->data_path_dir == BLE_HCI_ISO_DATA_PATH_DIR_INPUT) { + /* Input (Host to Controller) */ + tx = conn->tx; + if (tx == NULL || tx->data_path != NULL) { + return BLE_ERR_CMD_DISALLOWED; + } + if (tx->params->bn == 0) { + return BLE_ERR_UNSUPPORTED; + } - /* We do not (yet) support any vendor-specific data path */ - if (cmd->data_path_id) { - return BLE_ERR_CMD_DISALLOWED; - } + tx->data_path = data_path; + } else { + /* Output (Controller to Host) */ + rx = conn->rx; + if (rx == NULL || rx->data_path != NULL) { + return BLE_ERR_CMD_DISALLOWED; + } + if (rx->params->bn == 0) { + return BLE_ERR_UNSUPPORTED; + } - conn->data_path.enabled = 1; - conn->data_path.data_path_id = cmd->data_path_id; + rx->data_path = data_path; + ble_ll_isoal_demux_cb_set(&rx->demux, &isoal_demux_cb); + } rsp->conn_handle = cmd->conn_handle; + *rsplen = sizeof(*rsp); - return 0; + return BLE_ERR_SUCCESS; } int @@ -80,121 +291,263 @@ ble_ll_iso_remove_iso_data_path(const uint8_t *cmdbuf, uint8_t cmdlen, const struct ble_hci_le_remove_iso_data_path_cp *cmd = (const void *)cmdbuf; struct ble_hci_le_remove_iso_data_path_rp *rsp = (void *)rspbuf; struct ble_ll_iso_conn *conn; + struct ble_ll_iso_rx *rx; + struct ble_ll_iso_tx *tx; uint16_t conn_handle; + bool remove_rx; + bool remove_tx; + int rc; + + if (cmdlen != sizeof(*cmd)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } conn_handle = le16toh(cmd->conn_handle); conn = ble_ll_iso_conn_find_by_handle(conn_handle); if (!conn) { - return BLE_ERR_UNK_CONN_ID; + rc = BLE_ERR_UNK_CONN_ID; + goto done; } - /* Only input for now since we only support BIS */ - if (cmd->data_path_dir) { - return BLE_ERR_CMD_DISALLOWED; + /* Input (Host to Controller) */ + tx = conn->tx; + remove_tx = cmd->data_path_dir & (1 << BLE_HCI_ISO_DATA_PATH_DIR_INPUT); + if (remove_tx && (tx == NULL || tx->data_path == NULL)) { + rc = BLE_ERR_CMD_DISALLOWED; + goto done; + } + + /* Output (Controller to Host) */ + rx = conn->rx; + remove_rx = cmd->data_path_dir & (1 << BLE_HCI_ISO_DATA_PATH_DIR_OUTPUT); + if (remove_rx && (rx == NULL || rx->data_path == NULL)) { + rc = BLE_ERR_CMD_DISALLOWED; + goto done; + } + + if (remove_tx) { + BLE_LL_ASSERT(tx != NULL); + tx->data_path = NULL; } - conn->data_path.enabled = 0; + if (remove_rx) { + BLE_LL_ASSERT(rx != NULL); + ble_ll_isoal_demux_cb_set(&rx->demux, NULL); + rx->data_path = NULL; + } + rc = BLE_ERR_SUCCESS; +done: rsp->conn_handle = cmd->conn_handle; + *rsplen = sizeof(*rsp); - return 0; + return rc; } int -ble_ll_iso_read_tx_sync(const uint8_t *cmdbuf, uint8_t cmdlen, - uint8_t *rspbuf, uint8_t *rsplen) +ble_ll_iso_transmit_test(const uint8_t *cmdbuf, uint8_t cmdlen, + uint8_t *rspbuf, uint8_t *rsplen) { - const struct ble_hci_le_read_iso_tx_sync_cp *cmd = (const void *)cmdbuf; - struct ble_hci_le_read_iso_tx_sync_rp *rsp = (void *)rspbuf; - struct ble_ll_iso_conn *iso_conn; + const struct ble_hci_le_iso_receive_test_cp *cmd = (const void *)cmdbuf; + struct ble_hci_le_iso_receive_test_rp *rsp = (void *)rspbuf; + struct ble_ll_iso_conn *conn; + struct ble_ll_iso_tx *tx; uint16_t handle; + if (cmdlen != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + handle = le16toh(cmd->conn_handle); - iso_conn = ble_ll_iso_conn_find_by_handle(handle); - if (!iso_conn) { + + conn = ble_ll_iso_conn_find_by_handle(handle); + if (conn == NULL) { return BLE_ERR_UNK_CONN_ID; } + tx = conn->tx; + if (tx == NULL || tx->params->bn == 0) { + return BLE_ERR_UNSUPPORTED; + } + + if (tx->data_path != NULL) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (cmd->payload_type > BLE_HCI_PAYLOAD_TYPE_MAXIMUM_LENGTH) { + return BLE_ERR_INV_LMP_LL_PARM; + } + + tx->test.payload_type = cmd->payload_type; + tx->data_path = &test_iso_data_path_cb; + rsp->conn_handle = cmd->conn_handle; - rsp->packet_seq_num = htole16(iso_conn->mux.last_tx_packet_seq_num); - rsp->tx_timestamp = htole32(iso_conn->mux.last_tx_timestamp); - put_le24(rsp->time_offset, 0); *rsplen = sizeof(*rsp); - return 0; + return BLE_ERR_SUCCESS; } int -ble_ll_iso_transmit_test(const uint8_t *cmdbuf, uint8_t cmdlen, uint8_t *rspbuf, uint8_t *rsplen) +ble_ll_iso_receive_test(const uint8_t *cmdbuf, uint8_t cmdlen, uint8_t *rspbuf, + uint8_t *rsplen) { - const struct ble_hci_le_iso_transmit_test_cp *cmd = (const void *)cmdbuf; + const struct ble_hci_le_iso_receive_test_cp *cmd = (const void *)cmdbuf; struct ble_hci_le_iso_transmit_test_rp *rsp = (void *)rspbuf; struct ble_ll_iso_conn *conn; + struct ble_ll_iso_rx *rx; uint16_t handle; + if (cmdlen != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + handle = le16toh(cmd->conn_handle); conn = ble_ll_iso_conn_find_by_handle(handle); - if (!conn) { + if (conn == NULL) { return BLE_ERR_UNK_CONN_ID; } - if (conn->mux.bn == 0) { + rx = conn->rx; + if (rx == NULL || rx->params->bn == 0) { return BLE_ERR_UNSUPPORTED; } - if (conn->data_path.enabled) { + if (rx->data_path != NULL) { return BLE_ERR_CMD_DISALLOWED; } - if (cmd->payload_type > BLE_HCI_PAYLOAD_TYPE_MAXIMUM_LENGTH) { - return BLE_ERR_INV_LMP_LL_PARM; + rx->test.payload_type = cmd->payload_type; + rx->test.received_sdu_count = 0; + rx->test.missed_sdu_count = 0; + rx->test.failed_sdu_count = 0; + rx->data_path = &test_iso_data_path_cb; + + rsp->conn_handle = cmd->conn_handle; + + *rsplen = sizeof(*rsp); + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_iso_read_counters_test(const uint8_t *cmdbuf, uint8_t cmdlen, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_iso_read_test_counters_cp *cmd = (const void *)cmdbuf; + struct ble_hci_le_iso_read_test_counters_rp *rsp = (void *)rspbuf; + struct ble_ll_iso_conn *conn; + struct ble_ll_iso_rx *rx; + uint16_t handle; + + if (cmdlen != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + handle = le16toh(cmd->conn_handle); + + conn = ble_ll_iso_conn_find_by_handle(handle); + if (conn == NULL) { + return BLE_ERR_UNK_CONN_ID; } - conn->data_path.enabled = 1; - conn->data_path.data_path_id = BLE_HCI_ISO_DATA_PATH_ID_HCI; - conn->test_mode.transmit.enabled = 1; - conn->test_mode.transmit.payload_type = cmd->payload_type; + rx = conn->rx; + if (rx == NULL || rx->data_path != &test_iso_data_path_cb) { + return BLE_ERR_UNSUPPORTED; + } rsp->conn_handle = cmd->conn_handle; + rsp->received_sdu_count = rx->test.received_sdu_count; + rsp->missed_sdu_count = rx->test.missed_sdu_count; + rsp->failed_sdu_count = rx->test.failed_sdu_count; *rsplen = sizeof(*rsp); - return 0; + return BLE_ERR_SUCCESS; } int -ble_ll_iso_end_test(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, uint8_t *rsplen) +ble_ll_iso_end_test(const uint8_t *cmdbuf, uint8_t cmdlen, uint8_t *rspbuf, + uint8_t *rsplen) { const struct ble_hci_le_iso_test_end_cp *cmd = (const void *)cmdbuf; struct ble_hci_le_iso_test_end_rp *rsp = (void *)rspbuf; - struct ble_ll_iso_conn *iso_conn; + struct ble_ll_iso_conn *conn; + struct ble_ll_iso_rx *rx; + struct ble_ll_iso_tx *tx; uint16_t handle; + int rc; + + if (cmdlen != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } handle = le16toh(cmd->conn_handle); - iso_conn = ble_ll_iso_conn_find_by_handle(handle); - if (!iso_conn) { + + conn = ble_ll_iso_conn_find_by_handle(handle); + if (conn == NULL) { return BLE_ERR_UNK_CONN_ID; } - if (!iso_conn->test_mode.transmit.enabled) { - return BLE_ERR_UNSUPPORTED; + rc = BLE_ERR_UNSUPPORTED; + + memset(rsp, 0, sizeof(*rsp)); + rsp->conn_handle = htole16(handle); + + rx = conn->rx; + if (rx != NULL && rx->data_path == &test_iso_data_path_cb) { + rx->data_path = NULL; + rsp->received_sdu_count = htole32(rx->test.received_sdu_count); + rsp->missed_sdu_count = htole32(rx->test.missed_sdu_count); + rsp->failed_sdu_count = htole32(rx->test.failed_sdu_count); + rc = BLE_ERR_SUCCESS; + } + + tx = conn->tx; + if (tx != NULL && tx->data_path == &test_iso_data_path_cb) { + tx->data_path = NULL; + rc = BLE_ERR_SUCCESS; } - iso_conn->data_path.enabled = 0; - iso_conn->test_mode.transmit.enabled = 0; + *rsplen = sizeof(*rsp); + + return rc; +} + +int +ble_ll_iso_read_tx_sync(const uint8_t *cmdbuf, uint8_t cmdlen, uint8_t *rspbuf, + uint8_t *rsplen) +{ + const struct ble_hci_le_read_iso_tx_sync_cp *cmd = (const void *)cmdbuf; + struct ble_hci_le_read_iso_tx_sync_rp *rsp = (void *)rspbuf; + struct ble_ll_iso_conn *conn; + struct ble_ll_iso_tx *tx; + uint16_t handle; + + handle = le16toh(cmd->conn_handle); + + conn = ble_ll_iso_conn_find_by_handle(handle); + if (conn == NULL) { + return BLE_ERR_UNK_CONN_ID; + } + + tx = conn->tx; + if (tx == NULL) { + return BLE_ERR_UNSUPPORTED; + } rsp->conn_handle = cmd->conn_handle; - rsp->received_sdu_count = 0; - rsp->missed_sdu_count = 0; - rsp->failed_sdu_count = 0; + rsp->packet_seq_num = htole16(tx->mux.last_tx_packet_seq_num); + rsp->tx_timestamp = htole32(tx->mux.last_tx_timestamp); + put_le24(rsp->time_offset, 0); *rsplen = sizeof(*rsp); - return 0; + return BLE_ERR_SUCCESS; } struct ble_ll_iso_conn * @@ -214,23 +567,30 @@ ble_ll_iso_conn_find_by_handle(uint16_t conn_handle) void ble_ll_iso_init(void) { + int rc; + + rc = os_mempool_init(&hci_iso_data_pkt_mempool, HCI_ISO_BUF_COUNT, HCI_ISO_BUF_SIZE, + &hci_iso_data_pkt_membuf[0], "iso_data_pkt_mempool"); + BLE_LL_ASSERT(rc == 0); + + rc = os_mbuf_pool_init(&hci_iso_data_pkt_mbuf_pool, &hci_iso_data_pkt_mempool, + HCI_ISO_BUF_SIZE, HCI_ISO_BUF_COUNT); + BLE_LL_ASSERT(rc == 0); + STAILQ_INIT(&ll_iso_conn_q); - ble_ll_isoal_init(); } void ble_ll_iso_reset(void) -{ - STAILQ_INIT(&ll_iso_conn_q); - ble_ll_isoal_reset(); -} +{} int -ble_ll_iso_data_in(struct os_mbuf *om) +ble_ll_hci_iso_data_in(struct os_mbuf *om) { struct ble_hci_iso *hci_iso; struct ble_hci_iso_data *hci_iso_data; struct ble_ll_iso_conn *conn; + struct ble_ll_iso_tx *tx; struct ble_mbuf_hdr *blehdr; uint16_t data_hdr_len; uint16_t handle; @@ -254,11 +614,17 @@ ble_ll_iso_data_in(struct os_mbuf *om) return BLE_ERR_UNK_CONN_ID; } + tx = conn->tx; + if (tx == NULL) { + os_mbuf_free_chain(om); + return BLE_ERR_UNSUPPORTED; + } + data_hdr_len = 0; if ((pb_flag == BLE_HCI_ISO_PB_FIRST) || (pb_flag == BLE_HCI_ISO_PB_COMPLETE)) { blehdr = BLE_MBUF_HDR_PTR(om); - blehdr->txiso.packet_seq_num = ++conn->mux.sdu_counter; + blehdr->txiso.packet_seq_num = ++tx->mux.sdu_counter; blehdr->txiso.cpu_timestamp = ble_ll_tmr_get(); if (ts_flag) { @@ -303,41 +669,45 @@ ble_ll_iso_data_in(struct os_mbuf *om) } if (om) { - ble_ll_isoal_mux_sdu_enqueue(&conn->mux, om); + ble_ll_isoal_mux_sdu_put(&tx->mux, om); } return 0; } static int -ble_ll_iso_test_pdu_get(struct ble_ll_iso_conn *conn, uint8_t idx, uint32_t pkt_counter, uint8_t *llid, uint8_t *dptr) +ble_ll_iso_test_pdu_get(struct ble_ll_isoal_mux *mux, uint8_t idx, + uint32_t pkt_counter, uint8_t *llid, uint8_t *dptr, + uint8_t payload_type, uint32_t rand) { + struct ble_ll_iso_tx *tx; uint32_t payload_len; uint16_t rem_len; uint8_t sdu_idx; uint8_t pdu_idx; int pdu_len; - BLE_LL_ASSERT(!conn->mux.framed); + tx = CONTAINER_OF(mux, struct ble_ll_iso_tx, mux); + BLE_LL_ASSERT(!tx->params->framed); - sdu_idx = idx / conn->mux.pdu_per_sdu; - pdu_idx = idx - sdu_idx * conn->mux.pdu_per_sdu; + sdu_idx = idx / mux->pdu_per_sdu; + pdu_idx = idx - sdu_idx * mux->pdu_per_sdu; - switch (conn->test_mode.transmit.payload_type) { + switch (payload_type) { case BLE_HCI_PAYLOAD_TYPE_ZERO_LENGTH: *llid = 0b00; pdu_len = 0; break; case BLE_HCI_PAYLOAD_TYPE_VARIABLE_LENGTH: - payload_len = max(conn->test_mode.transmit.rand + (sdu_idx * pdu_idx), 4); + payload_len = max(rand + (sdu_idx * pdu_idx), 4); - rem_len = payload_len - pdu_idx * conn->mux.max_pdu; + rem_len = payload_len - pdu_idx * tx->params->max_pdu; if (rem_len == 0) { *llid = 0b01; pdu_len = 0; } else { - *llid = rem_len > conn->mux.max_pdu; - pdu_len = min(conn->mux.max_pdu, rem_len); + *llid = rem_len > tx->params->max_pdu; + pdu_len = min(tx->params->max_pdu, rem_len); } memset(dptr, 0, pdu_len); @@ -348,15 +718,15 @@ ble_ll_iso_test_pdu_get(struct ble_ll_iso_conn *conn, uint8_t idx, uint32_t pkt_ break; case BLE_HCI_PAYLOAD_TYPE_MAXIMUM_LENGTH: - payload_len = conn->max_sdu; + payload_len = tx->params->max_sdu; - rem_len = payload_len - pdu_idx * conn->mux.max_pdu; + rem_len = payload_len - pdu_idx * tx->params->max_pdu; if (rem_len == 0) { *llid = 0b01; pdu_len = 0; } else { - *llid = rem_len > conn->mux.max_pdu; - pdu_len = min(conn->mux.max_pdu, rem_len); + *llid = rem_len > tx->params->max_pdu; + pdu_len = min(tx->params->max_pdu, rem_len); } memset(dptr, 0, pdu_len); @@ -374,64 +744,163 @@ ble_ll_iso_test_pdu_get(struct ble_ll_iso_conn *conn, uint8_t idx, uint32_t pkt_ } int -ble_ll_iso_pdu_get(struct ble_ll_iso_conn *conn, uint8_t idx, uint32_t pkt_counter, uint8_t *llid, void *dptr) +ble_ll_iso_tx_pdu_get(struct ble_ll_iso_tx *tx, uint8_t idx, + uint32_t pkt_counter, uint8_t *llid, void *dptr) { - if (conn->test_mode.transmit.enabled) { - return ble_ll_iso_test_pdu_get(conn, idx, pkt_counter, llid, dptr); + if (tx->data_path == &test_iso_data_path_cb) { + return ble_ll_iso_test_pdu_get(&tx->mux, idx, pkt_counter, llid, dptr, + tx->test.payload_type, tx->test.rand); } - return ble_ll_isoal_mux_pdu_get(&conn->mux, idx, llid, dptr); + return ble_ll_isoal_mux_pdu_get(&tx->mux, idx, llid, dptr); } -void -ble_ll_iso_conn_init(struct ble_ll_iso_conn *conn, struct ble_ll_iso_conn_init_param *param) +static void +ble_ll_iso_conn_register(struct ble_ll_iso_conn *conn) { os_sr_t sr; - memset(conn, 0, sizeof(*conn)); - - conn->handle = param->conn_handle; - conn->max_sdu = param->max_sdu; - - ble_ll_isoal_mux_init(&conn->mux, param->max_pdu, param->iso_interval_us, param->sdu_interval_us, - param->bn, param->pte, BLE_LL_ISOAL_MUX_IS_FRAMED(param->framing), - param->framing == BLE_HCI_ISO_FRAMING_FRAMED_UNSEGMENTED); - OS_ENTER_CRITICAL(sr); STAILQ_INSERT_TAIL(&ll_iso_conn_q, conn, iso_conn_q_next); OS_EXIT_CRITICAL(sr); } -void -ble_ll_iso_conn_free(struct ble_ll_iso_conn *conn) +static void +ble_ll_iso_conn_unregister(struct ble_ll_iso_conn *conn) { os_sr_t sr; OS_ENTER_CRITICAL(sr); STAILQ_REMOVE(&ll_iso_conn_q, conn, ble_ll_iso_conn, iso_conn_q_next); OS_EXIT_CRITICAL(sr); +} + +void +ble_ll_iso_conn_init(struct ble_ll_iso_conn *conn, uint16_t conn_handle, + struct ble_ll_iso_rx *rx, struct ble_ll_iso_tx *tx) +{ + memset(conn, 0, sizeof(*conn)); + conn->handle = conn_handle; + conn->rx = rx; + conn->tx = tx; - ble_ll_isoal_mux_free(&conn->mux); + ble_ll_iso_conn_register(conn); } -int -ble_ll_iso_conn_event_start(struct ble_ll_iso_conn *conn, uint32_t timestamp) +void +ble_ll_iso_conn_reset(struct ble_ll_iso_conn *conn) { - if (conn->test_mode.transmit.enabled) { - conn->test_mode.transmit.rand = ble_ll_rand() % conn->max_sdu; + ble_ll_iso_conn_unregister(conn); + + if (conn->rx != NULL) { + ble_ll_iso_rx_reset(conn->rx); + } + + if (conn->tx != NULL) { + ble_ll_iso_tx_reset(conn->tx); + } + + if (conn->frag != NULL) { + os_mbuf_free_chain(conn->frag); + conn->frag = NULL; } +} + +static void +ble_ll_iso_params_to_isoal_config(const struct ble_ll_iso_params *params, + struct ble_ll_isoal_config *config) +{ + memset(config, 0, sizeof(*config)); + config->iso_interval_us = params->iso_interval * 1250; + config->sdu_interval_us = params->sdu_interval; + config->max_sdu = params->max_sdu; + config->max_pdu = params->max_pdu; + config->bn = params->bn; + config->pte = params->pte; + config->framed = params->framed; + config->unsegmented = params->framing_mode; +} + +void +ble_ll_iso_tx_init(struct ble_ll_iso_tx *tx, struct ble_ll_iso_conn *conn, + const struct ble_ll_iso_params *params) +{ + struct ble_ll_isoal_config config; + + memset(tx, 0, sizeof(*tx)); + tx->conn = conn; + tx->params = params; + + ble_ll_iso_params_to_isoal_config(params, &config); + + ble_ll_isoal_mux_init(&tx->mux, &config); +} + +void +ble_ll_iso_tx_reset(struct ble_ll_iso_tx *tx) +{ + ble_ll_isoal_mux_reset(&tx->mux); +} - ble_ll_isoal_mux_event_start(&conn->mux, timestamp); +int +ble_ll_iso_tx_event_start(struct ble_ll_iso_tx *tx, uint32_t timestamp) +{ + ble_ll_isoal_mux_event_start(&tx->mux, timestamp); return 0; } int -ble_ll_iso_conn_event_done(struct ble_ll_iso_conn *conn) +ble_ll_iso_tx_event_done(struct ble_ll_iso_tx *tx) +{ + tx->conn->num_completed_pkt += ble_ll_isoal_mux_event_done(&tx->mux); + + return tx->conn->num_completed_pkt; +} + +void +ble_ll_iso_rx_init(struct ble_ll_iso_rx *rx, struct ble_ll_iso_conn *conn, + const struct ble_ll_iso_params *params) +{ + struct ble_ll_isoal_config config; + + memset(rx, 0, sizeof(*rx)); + rx->conn = conn; + rx->params = params; + + ble_ll_iso_params_to_isoal_config(params, &config); + + ble_ll_isoal_demux_init(&rx->demux, &config); +} + +void +ble_ll_iso_rx_reset(struct ble_ll_iso_rx *rx) { - conn->num_completed_pkt += ble_ll_isoal_mux_event_done(&conn->mux); + ble_ll_isoal_demux_reset(&rx->demux); +} - return conn->num_completed_pkt; +int +ble_ll_iso_rx_event_start(struct ble_ll_iso_rx *rx, uint32_t timestamp) +{ + ble_ll_isoal_demux_event_start(&rx->demux, timestamp); + + return 0; +} + +int +ble_ll_iso_rx_event_done(struct ble_ll_iso_rx *rx) +{ + ble_ll_isoal_demux_event_done(&rx->demux); + + return 0; +} + +int +ble_ll_iso_rx_pdu_put(struct ble_ll_iso_rx *rx, uint8_t idx, struct os_mbuf *om) +{ + ble_ll_isoal_demux_pdu_put(&rx->demux, idx, om); + + return 0; } #endif /* BLE_LL_ISO */ diff --git a/nimble/controller/src/ble_ll_iso_big.c b/nimble/controller/src/ble_ll_iso_big.c index 5b907bc61a..f40a898e92 100644 --- a/nimble/controller/src/ble_ll_iso_big.c +++ b/nimble/controller/src/ble_ll_iso_big.c @@ -36,6 +36,7 @@ #include #include #include "ble_ll_priv.h" +#include "ble_ll_iso_big_priv.h" #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) @@ -48,33 +49,6 @@ #define BIG_CONTROL_ACTIVE_CHAN_MAP 1 #define BIG_CONTROL_ACTIVE_TERM 2 -struct ble_ll_iso_big; - -struct ble_ll_iso_bis { - struct ble_ll_iso_big *big; - uint8_t num; - - uint32_t aa; - uint32_t crc_init; - uint16_t chan_id; - uint8_t iv[8]; - - struct { - uint16_t prn_sub_lu; - uint16_t remap_idx; - - uint8_t subevent_num; - uint8_t n; - uint8_t g; - } tx; - - struct ble_ll_iso_conn conn; - - STAILQ_ENTRY(ble_ll_iso_bis) bis_q_next; -}; - -STAILQ_HEAD(ble_ll_iso_bis_q, ble_ll_iso_bis); - struct big_params { uint8_t nse; /* 1-31 */ uint8_t bn; /* 1-7, mandatory 1 */ @@ -92,84 +66,13 @@ struct big_params { uint8_t broadcast_code[16]; }; -struct ble_ll_iso_big { - struct ble_ll_adv_sm *advsm; - - uint8_t handle; - uint8_t num_bis; - uint16_t iso_interval; - uint16_t bis_spacing; - uint16_t sub_interval; - uint8_t phy; - uint8_t max_pdu; - uint16_t max_sdu; - uint16_t mpt; - uint8_t bn; /* 1-7, mandatory 1 */ - uint8_t pto; /* 0-15, mandatory 0 */ - uint8_t irc; /* 1-15 */ - uint8_t nse; /* 1-31 */ - uint8_t interleaved : 1; - uint8_t framed : 1; - uint8_t framing_mode : 1; - uint8_t encrypted : 1; - uint8_t giv[8]; - uint8_t gskd[16]; - uint8_t gsk[16]; - uint8_t iv[8]; - uint8_t gc; - - uint32_t sdu_interval; - - uint32_t ctrl_aa; - uint16_t crc_init; - uint8_t chan_map[BLE_LL_CHAN_MAP_LEN]; - uint8_t chan_map_used; - - uint8_t biginfo[33]; - - uint64_t big_counter; - uint64_t bis_counter; - - uint32_t sync_delay; - uint32_t transport_latency_us; - uint32_t event_start; - uint8_t event_start_us; - uint32_t anchor_base_ticks; - uint8_t anchor_base_rem_us; - uint16_t anchor_offset; - struct ble_ll_sched_item sch; - struct ble_npl_event event_done; - - struct { - uint16_t subevents_rem; - struct ble_ll_iso_bis *bis; - } tx; - - struct ble_ll_iso_bis_q bis_q; - -#if MYNEWT_VAL(BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS) - uint32_t last_feedback; -#endif - - uint8_t cstf : 1; - uint8_t cssn : 4; - uint8_t control_active : 3; - uint16_t control_instant; - - uint8_t chan_map_new_pending : 1; - uint8_t chan_map_new[BLE_LL_CHAN_MAP_LEN]; - - uint8_t term_pending : 1; - uint8_t term_reason : 7; -}; - static struct ble_ll_iso_big big_pool[BIG_POOL_SIZE]; static struct ble_ll_iso_bis bis_pool[BIS_POOL_SIZE]; static uint8_t big_pool_free = BIG_POOL_SIZE; static uint8_t bis_pool_free = BIS_POOL_SIZE; static struct ble_ll_iso_big *big_pending; -static struct ble_ll_iso_big *big_active; +static struct ble_ll_iso_big *g_ble_ll_iso_big_curr; static void big_sched_set(struct ble_ll_iso_big *big) @@ -353,7 +256,7 @@ ble_ll_iso_big_free(struct ble_ll_iso_big *big) ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &big->event_done); STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { - ble_ll_iso_conn_free(&bis->conn); + ble_ll_iso_conn_reset(&bis->conn); bis->big = NULL; bis_pool_free++; } @@ -466,7 +369,7 @@ ble_ll_iso_big_event_done(struct ble_ll_iso_big *big) #endif STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { - num_completed_pkt = ble_ll_iso_conn_event_done(&bis->conn); + num_completed_pkt = ble_ll_iso_tx_event_done(&bis->tx); if (hci_ev && num_completed_pkt) { idx = hci_ev_ncp->count++; hci_ev_ncp->completed[idx].handle = htole16(bis->conn.handle); @@ -576,7 +479,7 @@ ble_ll_iso_big_event_done_ev(struct ble_npl_event *ev) static void ble_ll_iso_big_event_done_to_ll(struct ble_ll_iso_big *big) { - big_active = NULL; + g_ble_ll_iso_big_curr = NULL; ble_ll_state_set(BLE_LL_STATE_STANDBY); ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &big->event_done); } @@ -668,13 +571,13 @@ ble_ll_iso_big_subevent_pdu_cb(uint8_t *dptr, void *arg, uint8_t *hdr_byte) uint8_t pdu_len; big = arg; - bis = big->tx.bis; + bis = big->bis; /* Core 5.3, Vol 6, Part B, 4.4.6.6 */ - if (bis->tx.g < big->irc) { - idx = bis->tx.n; + if (bis->g < big->irc) { + idx = bis->n; } else { - idx = big->bn * big->pto * (bis->tx.g - big->irc + 1) + bis->tx.n; + idx = big->bn * big->pto * (bis->g - big->irc + 1) + bis->n; } if (big->encrypted) { @@ -684,7 +587,8 @@ ble_ll_iso_big_subevent_pdu_cb(uint8_t *dptr, void *arg, uint8_t *hdr_byte) } #if 1 - pdu_len = ble_ll_iso_pdu_get(&bis->conn, idx, big->bis_counter + idx, &llid, dptr); + pdu_len = + ble_ll_iso_tx_pdu_get(&bis->tx, idx, big->bis_counter + idx, &llid, dptr); #else llid = 0; pdu_len = big->max_pdu; @@ -692,12 +596,12 @@ ble_ll_iso_big_subevent_pdu_cb(uint8_t *dptr, void *arg, uint8_t *hdr_byte) memset(dptr, bis->num | (bis->num << 4), pdu_len); put_be32(dptr, big->big_counter); put_be32(&dptr[4], big->bis_counter + idx); - dptr[8] = bis->tx.subevent_num; - dptr[9] = bis->tx.g; - dptr[10] = bis->tx.n; - if (bis->tx.g == 0) { + dptr[8] = bis->subevent_num; + dptr[9] = bis->g; + dptr[10] = bis->n; + if (bis->g == 0) { dptr[11] = 'B'; - } else if (bis->tx.g < big->irc) { + } else if (bis->g < big->irc) { dptr[11] = 'R'; } else { dptr[11] = 'P'; @@ -718,25 +622,21 @@ ble_ll_iso_big_subevent_tx(struct ble_ll_iso_big *big) int to_tx; int rc; - bis = big->tx.bis; + bis = big->bis; - if (bis->tx.subevent_num == 1) { + if (bis->subevent_num == 1) { chan_idx = ble_ll_utils_dci_iso_event(big->big_counter, bis->chan_id, - &bis->tx.prn_sub_lu, - big->chan_map_used, - big->chan_map, - &bis->tx.remap_idx); + &bis->prn_sub_lu, big->chan_map_used, + big->chan_map, &bis->remap_idx); } else { - chan_idx = ble_ll_utils_dci_iso_subevent(bis->chan_id, - &bis->tx.prn_sub_lu, + chan_idx = ble_ll_utils_dci_iso_subevent(bis->chan_id, &bis->prn_sub_lu, big->chan_map_used, - big->chan_map, - &bis->tx.remap_idx); + big->chan_map, &bis->remap_idx); } ble_phy_setchan(chan_idx, bis->aa, bis->crc_init); - to_tx = (big->tx.subevents_rem > 1) || big->cstf; + to_tx = (big->subevents_rem > 1) || big->cstf; ble_phy_transition_set( to_tx ? BLE_PHY_TRANSITION_TO_TX_ISO_SUBEVENT : BLE_PHY_TRANSITION_NONE, @@ -754,29 +654,29 @@ ble_ll_iso_big_subevent_txend_cb(void *arg) int rc; big = arg; - bis = big->tx.bis; + bis = big->bis; - bis->tx.n++; - if (bis->tx.n == big->bn) { - bis->tx.n = 0; - bis->tx.g++; + bis->n++; + if (bis->n == big->bn) { + bis->n = 0; + bis->g++; } /* Switch to next BIS if interleaved or all subevents for current BIS were * transmitted. */ - if (big->interleaved || (bis->tx.subevent_num == big->nse)) { + if (big->interleaved || (bis->subevent_num == big->nse)) { bis = STAILQ_NEXT(bis, bis_q_next); if (!bis) { bis = STAILQ_FIRST(&big->bis_q); } - big->tx.bis = bis; + big->bis = bis; } - bis->tx.subevent_num++; - big->tx.subevents_rem--; + bis->subevent_num++; + big->subevents_rem--; - if (big->tx.subevents_rem > 0) { + if (big->subevents_rem > 0) { rc = ble_ll_iso_big_subevent_tx(big); if (rc) { ble_phy_disable(); @@ -810,7 +710,7 @@ ble_ll_iso_big_event_sched_cb(struct ble_ll_sched_item *sch) big = sch->cb_arg; ble_ll_state_set(BLE_LL_STATE_BIG); - big_active = big; + g_ble_ll_iso_big_curr = big; ble_ll_whitelist_disable(); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) @@ -824,20 +724,19 @@ ble_ll_iso_big_event_sched_cb(struct ble_ll_sched_item *sch) ble_ll_tx_power_set(g_ble_ll_tx_power); /* XXX calculate this in advance at the end of previous event? */ - big->tx.subevents_rem = big->num_bis * big->nse; + big->subevents_rem = big->num_bis * big->nse; STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { - ble_ll_iso_conn_event_start(&bis->conn, (uint64_t)big->event_start * - 1000000 / 32768 + + ble_ll_iso_tx_event_start(&bis->tx, ble_ll_tmr_t2u(big->event_start) + big->event_start_us); - bis->tx.subevent_num = 0; - bis->tx.n = 0; - bis->tx.g = 0; + bis->subevent_num = 0; + bis->n = 0; + bis->g = 0; } /* Select 1st BIS for transmission */ - big->tx.bis = STAILQ_FIRST(&big->bis_q); - big->tx.bis->tx.subevent_num = 1; + big->bis = STAILQ_FIRST(&big->bis_q); + big->bis->subevent_num = 1; rc = ble_phy_tx_set_start_time(sch->start_time + g_ble_ll_sched_offset_ticks, sch->remainder); @@ -865,75 +764,19 @@ ble_ll_iso_big_event_sched_cb(struct ble_ll_sched_item *sch) return BLE_LL_SCHED_STATE_RUNNING; } -static void -ble_ll_iso_big_calculate_gsk(struct ble_ll_iso_big *big, - const uint8_t *broadcast_code) -{ - static const uint8_t big1[16] = {0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x42, 0x49, 0x47, 0x31}; - static const uint8_t big2[4] = {0x42, 0x49, 0x47, 0x32}; - static const uint8_t big3[4] = {0x42, 0x49, 0x47, 0x33}; - uint8_t code[16]; - uint8_t igltk[16]; - uint8_t gltk[16]; - - /* broadcast code is lsb-first in hci, we need msb-first */ - swap_buf(code, broadcast_code, 16); - - ble_ll_rand_data_get(big->gskd, sizeof(big->gskd)); - - ble_ll_crypto_h7(big1, code, igltk); - ble_ll_crypto_h6(igltk, big2, gltk); - ble_ll_crypto_h8(gltk, big->gskd, big3, big->gsk); - - /* need gskd for biginfo, i.e. lsb-first */ - swap_in_place(big->gskd, 16); -} - -static void -ble_ll_iso_big_calculate_iv(struct ble_ll_iso_big *big) -{ - struct ble_ll_iso_bis *bis; - uint8_t *aa; - - ble_ll_rand_data_get(big->giv, sizeof(big->giv)); - STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { - memcpy(&bis->iv[4], &big->giv[4], 4); - aa = (uint8_t *)&bis->aa; - bis->iv[0] = big->giv[0] ^ aa[0]; - bis->iv[1] = big->giv[1] ^ aa[1]; - bis->iv[2] = big->giv[2] ^ aa[2]; - bis->iv[3] = big->giv[3] ^ aa[3]; - } - - memcpy(&big->iv[4], &big->giv[4], 4); - aa = (uint8_t *)&big->ctrl_aa; - big->iv[0] = big->giv[0] ^ aa[0]; - big->iv[1] = big->giv[1] ^ aa[1]; - big->iv[2] = big->giv[2] ^ aa[2]; - big->iv[3] = big->giv[3] ^ aa[3]; -} - static int ble_ll_iso_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, struct big_params *bp) { - struct ble_ll_iso_conn_init_param conn_init_param = { - .iso_interval_us = bp->iso_interval * 1250, - .sdu_interval_us = bp->sdu_interval, - .max_sdu = bp->max_sdu, - .max_pdu = bp->max_pdu, - .framing = bp->framing, - .bn = bp->bn, - }; + struct ble_ll_iso_params *iso_params; struct ble_ll_iso_big *big = NULL; struct ble_ll_iso_bis *bis; struct ble_ll_adv_sm *advsm; uint32_t seed_aa; + uint16_t conn_handle; uint8_t gc; uint8_t idx; + uint8_t pte; int rc; if ((big_pool_free == 0) || (bis_pool_free < num_bis)) { @@ -987,11 +830,36 @@ ble_ll_iso_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, /* Core 5.3, Vol 6, Part B, 4.4.6.6 */ gc = bp->nse / bp->bn; if (bp->irc == gc) { - conn_init_param.pte = 0; + pte = 0; } else { - conn_init_param.pte = bp->pto * (gc - bp->irc); + pte = bp->pto * (gc - bp->irc); } + big->bn = bp->bn; + big->pto = bp->pto; + big->irc = bp->irc; + big->nse = bp->nse; + big->interleaved = bp->interleaved; + big->framed = bp->framing != BLE_HCI_ISO_FRAMING_UNFRAMED; + big->framing_mode = bp->framing == BLE_HCI_ISO_FRAMING_FRAMED_UNSEGMENTED; + big->encrypted = bp->encrypted; + big->sdu_interval = bp->sdu_interval; + big->iso_interval = bp->iso_interval; + big->phy = bp->phy; + big->max_pdu = bp->max_pdu; + big->max_sdu = bp->max_sdu; + + /* TODO: Fix duplicated data */ + iso_params = &big->params; + iso_params->iso_interval = big->iso_interval; + iso_params->sdu_interval = big->sdu_interval; + iso_params->max_sdu = big->max_sdu; + iso_params->max_pdu = big->max_pdu; + iso_params->bn = big->bn; + iso_params->pte = pte; + iso_params->framed = big->framed; + iso_params->framing_mode = big->framing_mode; + /* Allocate BISes */ STAILQ_INIT(&big->bis_q); big->num_bis = 0; @@ -1008,26 +876,14 @@ ble_ll_iso_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, bis->num = big->num_bis; bis->crc_init = (big->crc_init << 8) | (big->num_bis); - conn_init_param.conn_handle = BLE_LL_CONN_HANDLE(BLE_LL_CONN_HANDLE_TYPE_BIS, idx); + conn_handle = BLE_LL_CONN_HANDLE(BLE_LL_CONN_HANDLE_TYPE_BIS, idx); - ble_ll_iso_conn_init(&bis->conn, &conn_init_param); + ble_ll_iso_conn_init(&bis->conn, conn_handle, NULL, &bis->tx); + ble_ll_iso_tx_init(&bis->tx, &bis->conn, &big->params); } bis_pool_free -= num_bis; - big->bn = bp->bn; - big->pto = bp->pto; - big->irc = bp->irc; - big->nse = bp->nse; - big->interleaved = bp->interleaved; - big->framed = bp->framing != BLE_HCI_ISO_FRAMING_UNFRAMED; - big->framing_mode = bp->framing == BLE_HCI_ISO_FRAMING_FRAMED_UNSEGMENTED; - big->encrypted = bp->encrypted; - big->sdu_interval = bp->sdu_interval; - big->iso_interval = bp->iso_interval; - big->phy = bp->phy; - big->max_pdu = bp->max_pdu; - big->max_sdu = bp->max_sdu; /* Core 5.3, Vol 6, Part B, 4.4.6.3 */ big->mpt = ble_ll_pdu_us(big->max_pdu + (big->encrypted ? 4 : 0), ble_ll_phy_to_phy_mode(big->phy, @@ -1095,7 +951,10 @@ ble_ll_iso_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, ble_ll_iso_big_biginfo_calc(big, seed_aa); if (big->encrypted) { + ble_ll_rand_data_get(big->gskd, sizeof(big->gskd)); ble_ll_iso_big_calculate_gsk(big, bp->broadcast_code); + + ble_ll_rand_data_get(big->giv, sizeof(big->giv)); ble_ll_iso_big_calculate_iv(big); } @@ -1200,8 +1059,8 @@ ble_ll_iso_big_chan_map_update(void) void ble_ll_iso_big_halt(void) { - if (big_active) { - ble_ll_iso_big_event_done_to_ll(big_active); + if (g_ble_ll_iso_big_curr) { + ble_ll_iso_big_event_done_to_ll(g_ble_ll_iso_big_curr); } } @@ -1507,3 +1366,50 @@ ble_ll_iso_big_reset(void) } #endif /* BLE_LL_ISO_BROADCASTER */ + +void +ble_ll_iso_big_calculate_gsk(struct ble_ll_iso_big *big, const uint8_t *broadcast_code) +{ + static const uint8_t big1[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x49, 0x47, 0x31 }; + static const uint8_t big2[4] = { 0x42, 0x49, 0x47, 0x32 }; + static const uint8_t big3[4] = { 0x42, 0x49, 0x47, 0x33 }; + uint8_t code[16]; + uint8_t igltk[16]; + uint8_t gltk[16]; + uint8_t gskd[16]; + + /* broadcast code is lsb-first in hci, we need msb-first */ + swap_buf(code, broadcast_code, 16); + + /* gskd is lsb-first in BigInfo, we need msb-first */ + swap_buf(gskd, big->gskd, 16); + + ble_ll_crypto_h7(big1, code, igltk); + ble_ll_crypto_h6(igltk, big2, gltk); + ble_ll_crypto_h8(gltk, gskd, big3, big->gsk); +} + +void +ble_ll_iso_big_calculate_iv(struct ble_ll_iso_big *big) +{ + struct ble_ll_iso_bis *bis; + uint8_t *aa; + + STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { + memcpy(&bis->iv[4], &big->giv[4], 4); + aa = (uint8_t *)&bis->aa; + bis->iv[0] = big->giv[0] ^ aa[0]; + bis->iv[1] = big->giv[1] ^ aa[1]; + bis->iv[2] = big->giv[2] ^ aa[2]; + bis->iv[3] = big->giv[3] ^ aa[3]; + } + + memcpy(&big->iv[4], &big->giv[4], 4); + aa = (uint8_t *)&big->ctrl_aa; + big->iv[0] = big->giv[0] ^ aa[0]; + big->iv[1] = big->giv[1] ^ aa[1]; + big->iv[2] = big->giv[2] ^ aa[2]; + big->iv[3] = big->giv[3] ^ aa[3]; +} diff --git a/nimble/controller/src/ble_ll_iso_big_priv.h b/nimble/controller/src/ble_ll_iso_big_priv.h new file mode 100644 index 0000000000..5063b07ce9 --- /dev/null +++ b/nimble/controller/src/ble_ll_iso_big_priv.h @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_ISO_BIG_PRIV_ +#define H_BLE_LL_ISO_BIG_PRIV_ + +#include "ble_ll_iso_priv.h" +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_ll_iso_big; + +struct ble_ll_iso_bis { + struct ble_ll_iso_big *big; + uint8_t num; + + uint32_t aa; + uint32_t crc_init; + uint16_t chan_id; + uint16_t chan_idx; + uint8_t iv[8]; + + uint16_t prn_sub_lu; + uint16_t remap_idx; + + /** + * Core 6.0, Vol 6, Part B, 4.4.6.6 + * The subevents of each BIS event are partitioned into groups of BN subevents each. + * The groups of subevents are numbered using g from 0 to GC – 1 in order, where GC = NSE ÷ BN. + */ + uint8_t subevent_num; + /* subevent number within group */ + uint8_t n; + /* group number */ + uint8_t g; + + union { + struct ble_ll_iso_rx rx; + struct ble_ll_iso_tx tx; + }; + struct ble_ll_iso_conn conn; + + STAILQ_ENTRY(ble_ll_iso_bis) bis_q_next; +}; + +STAILQ_HEAD(ble_ll_iso_bis_q, ble_ll_iso_bis); + +struct ble_ll_iso_big { + struct ble_ll_adv_sm *advsm; + + uint8_t handle; + uint8_t num_bis; + uint16_t iso_interval; + uint16_t bis_spacing; + uint16_t sub_interval; + uint8_t phy; + uint8_t max_pdu; + uint16_t max_sdu; + uint16_t mpt; + uint8_t bn; /* 1-7, mandatory 1 */ + uint8_t pto; /* 0-15, mandatory 0 */ + uint8_t irc; /* 1-15 */ + uint8_t nse; /* 1-31 */ + uint8_t interleaved : 1; + uint8_t framed : 1; + uint8_t framing_mode : 1; + uint8_t encrypted : 1; + uint8_t giv[8]; + uint8_t gskd[16]; + uint8_t gsk[16]; + uint8_t iv[8]; + uint8_t gc; + + uint32_t sdu_interval; + + uint32_t ctrl_aa; + uint16_t crc_init; + uint8_t chan_map[BLE_LL_CHAN_MAP_LEN]; + uint8_t chan_map_used; + + uint8_t biginfo[33]; + + uint64_t big_counter; + uint64_t bis_counter; + + uint32_t sync_delay; + uint32_t transport_latency_us; + uint32_t event_start; + uint8_t event_start_us; + uint32_t anchor_base_ticks; + uint8_t anchor_base_rem_us; + uint16_t anchor_offset; + struct ble_ll_sched_item sch; + struct ble_npl_event event_done; + + uint16_t subevents_rem; + struct ble_ll_iso_bis *bis; + + struct ble_ll_iso_bis_q bis_q; + +#if MYNEWT_VAL(BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS) + uint32_t last_feedback; +#endif + + uint8_t cstf : 1; + uint8_t cssn : 4; + uint8_t control_active : 3; + uint16_t control_instant; + + uint8_t chan_map_new_pending : 1; + uint8_t chan_map_new[BLE_LL_CHAN_MAP_LEN]; + + uint8_t term_pending : 1; + uint8_t term_reason : 7; + + uint32_t sync_timeout; + uint8_t mse; + + uint8_t sca; + uint16_t wfr_us; + + uint16_t tx_win_us; + + struct ble_ll_iso_params params; + + STAILQ_ENTRY(ble_ll_iso_big) big_q_next; +}; + +void ble_ll_iso_big_calculate_gsk(struct ble_ll_iso_big *big, + const uint8_t *broadcast_code); + +void ble_ll_iso_big_calculate_iv(struct ble_ll_iso_big *big); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_ISO_BIG_PRIV_ */ diff --git a/nimble/controller/src/ble_ll_iso_big_sync.c b/nimble/controller/src/ble_ll_iso_big_sync.c new file mode 100644 index 0000000000..1908115574 --- /dev/null +++ b/nimble/controller/src/ble_ll_iso_big_sync.c @@ -0,0 +1,1437 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "ble_ll_iso_priv.h" +#include "ble_ll_iso_big_priv.h" +#include "ble_ll_priv.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + +#define BIG_POOL_SIZE (MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC_MAX_BIG)) +#define BIS_POOL_SIZE \ + (MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC_MAX_BIG) * \ + MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC_MAX_BIS)) + +#define BIG_CONTROL_ACTIVE_CHAN_MAP 1 +#define BIG_CONTROL_ACTIVE_TERM 2 + +/* CSSN initial value, that is out of the allowed range (1-7) */ +#define CSSN_INITIAL 0xF + +#define BIG_SYNC_TIMEOUT_US(_sync_timeout) ((_sync_timeout) * 10 * 1000) + +struct ble_ll_iso_big_sync_params { + struct ble_ll_iso_big *big; + struct ble_ll_sync_sm *syncsm; + uint8_t broadcast_code[16]; + uint32_t bis_mask; +}; + +union iso_pdu_user_data { + struct { + uint8_t pdu_idx; + uint8_t big_handle; + }; + uint32_t value; +}; + +static os_membuf_t mb_big[OS_MEMPOOL_SIZE(BIG_POOL_SIZE, sizeof(struct ble_ll_iso_big))]; +static struct os_mempool mp_big; +static os_membuf_t mb_bis[OS_MEMPOOL_SIZE(BIS_POOL_SIZE, sizeof(struct ble_ll_iso_bis))]; +static struct os_mempool mp_bis; + +STAILQ_HEAD(ble_ll_iso_big_q, ble_ll_iso_big); +static struct ble_ll_iso_big_q big_q; + +static struct ble_ll_iso_big_sync_params g_ble_ll_iso_big_sync_params; +static struct ble_ll_iso_big *g_ble_ll_iso_big_curr; + +STATS_SECT_START(ble_ll_iso_big_sync_stats) + STATS_SECT_ENTRY(wfr_expirations) + STATS_SECT_ENTRY(no_big) + STATS_SECT_ENTRY(rx_ctrl_pdus) + STATS_SECT_ENTRY(rx_data_pdus) + STATS_SECT_ENTRY(rx_data_bytes) + STATS_SECT_ENTRY(rx_malformed_ctrl_pdus) +STATS_SECT_END +STATS_SECT_DECL(ble_ll_iso_big_sync_stats) ble_ll_iso_big_sync_stats; + +STATS_NAME_START(ble_ll_iso_big_sync_stats) + STATS_NAME(ble_ll_iso_big_sync_stats, wfr_expirations) + STATS_NAME(ble_ll_iso_big_sync_stats, no_big) + STATS_NAME(ble_ll_iso_big_sync_stats, rx_ctrl_pdus) + STATS_NAME(ble_ll_iso_big_sync_stats, rx_data_pdus) + STATS_NAME(ble_ll_iso_big_sync_stats, rx_data_bytes) + STATS_NAME(ble_ll_iso_big_sync_stats, rx_malformed_ctrl_pdus) +STATS_NAME_END(ble_ll_iso_big_sync_stats) + +static struct ble_ll_iso_big * +big_ll_iso_big_find(uint8_t big_handle) +{ + struct ble_ll_iso_big *big; + + STAILQ_FOREACH(big, &big_q, big_q_next) { + if (big->handle == big_handle) { + return big; + } + } + + return NULL; +} + +static struct ble_ll_iso_big * +ble_ll_iso_big_alloc(uint8_t big_handle) +{ + struct ble_ll_iso_big *big = NULL; + + big = os_memblock_get(&mp_big); + if (!big) { + return NULL; + } + + memset(big, 0, sizeof(*big)); + big->handle = big_handle; + STAILQ_INIT(&big->bis_q); + + STAILQ_INSERT_HEAD(&big_q, big, big_q_next); + + return big; +} + +static struct ble_ll_iso_bis * +ble_ll_iso_big_alloc_bis(struct ble_ll_iso_big *big) +{ + struct ble_ll_iso_bis *bis; + + bis = os_memblock_get(&mp_bis); + if (!bis) { + return NULL; + } + + memset(bis, 0, sizeof(*bis)); + bis->big = big; + big->num_bis++; + + STAILQ_INSERT_TAIL(&big->bis_q, bis, bis_q_next); + + return bis; +} + +static void +ble_ll_iso_big_sync_hci_evt_established(struct ble_ll_iso_big *big, uint8_t status) +{ + struct ble_hci_ev_le_subev_big_sync_established *evt; + struct ble_ll_iso_bis *bis; + struct ble_hci_ev *hci_ev; + uint8_t idx; + + hci_ev = ble_transport_alloc_evt(0); + if (!hci_ev) { + BLE_LL_ASSERT(0); + /* XXX should we retry later? */ + return; + } + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*evt) + big->num_bis * sizeof(evt->conn_handle[0]); + + evt = (void *)hci_ev->data; + memset(evt, 0, hci_ev->length); + evt->subev_code = BLE_HCI_LE_SUBEV_BIG_SYNC_ESTABLISHED; + evt->status = status; + + evt->big_handle = big->handle; + + if (status == 0) { + /* Core 5.3, Vol 6, Part G, 3.2.2 */ + put_le24(evt->transport_latency_big, + big->sync_delay + + (big->pto * (big->nse / big->bn - big->irc) + 1) * + big->iso_interval * 1250 - + big->sdu_interval); + evt->nse = big->nse; + evt->bn = big->bn; + evt->pto = big->pto; + evt->irc = big->irc; + evt->max_pdu = htole16(big->max_pdu); + evt->iso_interval = htole16(big->iso_interval); + evt->num_bis = big->num_bis; + } + + idx = 0; + STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { + evt->conn_handle[idx] = htole16(bis->conn.handle); + idx++; + } + + ble_ll_hci_event_send(hci_ev); +} + +static void +ble_ll_iso_big_sync_hci_evt_lost(struct ble_ll_iso_big *big) +{ + struct ble_hci_ev_le_subev_big_sync_lost *evt; + struct ble_hci_ev *hci_ev; + + hci_ev = ble_transport_alloc_evt(0); + if (!hci_ev) { + BLE_LL_ASSERT(0); + /* XXX should we retry later? */ + return; + } + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*evt); + + evt = (void *)hci_ev->data; + memset(evt, 0, hci_ev->length); + evt->subev_code = BLE_HCI_LE_SUBEV_BIG_SYNC_LOST; + evt->big_handle = big->handle; + evt->reason = big->term_reason; + + ble_ll_hci_event_send(hci_ev); +} + +static void +ble_ll_iso_big_free(struct ble_ll_iso_big *big) +{ + struct ble_ll_iso_bis *bis; + + ble_ll_sched_rmv_elem(&big->sch); + if (ble_npl_event_is_queued(&big->event_done)) { + ble_ll_event_remove(&big->event_done); + } + + bis = STAILQ_FIRST(&big->bis_q); + while (bis != NULL) { + ble_ll_iso_conn_reset(&bis->conn); + STAILQ_REMOVE_HEAD(&big->bis_q, bis_q_next); + os_memblock_put(&mp_bis, bis); + bis = STAILQ_FIRST(&big->bis_q); + } + + STAILQ_REMOVE(&big_q, big, ble_ll_iso_big, big_q_next); + os_memblock_put(&mp_big, big); +} + +static bool +ble_ll_iso_big_sync_pending(struct ble_ll_iso_big *big) +{ + struct ble_ll_iso_big_sync_params *sync_params = &g_ble_ll_iso_big_sync_params; + + return sync_params->big == big; +} + +static bool +ble_ll_iso_big_term_pending(struct ble_ll_iso_big *big) +{ + return big->term_reason != BLE_ERR_SUCCESS; +} + +static void +ble_ll_iso_big_sync_done(struct ble_ll_iso_big *big, uint8_t status) +{ + struct ble_ll_iso_big_sync_params *sync_params = &g_ble_ll_iso_big_sync_params; + + sync_params->big = NULL; + + ble_ll_iso_big_sync_hci_evt_established(big, status); + + /* Core 6.0 | Vol 4, Part E, 7.8.106 + * When the Controller establishes synchronization and if the + * BIG_Sync_Timeout set by the Host is less than 6 × ISO_Interval, the + * Controller shall set the timeout to 6 × ISO_Interval. + */ + big->sync_timeout = max(big->sync_timeout, 6 * big->iso_interval * 0.125); +} + +static int +ble_ll_iso_big_sync_phy_to_phy(uint8_t big_phy, uint8_t *phy) +{ + switch (big_phy) { + case 0: + *phy = BLE_PHY_1M; + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case 1: + *phy = BLE_PHY_2M; + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case 2: + *phy = BLE_PHY_CODED; + break; +#endif + default: + return -1; + } + + return 0; +} + +static uint8_t +msb_idx(uint32_t val) +{ + BLE_LL_ASSERT(val != 0); + return 31 - __builtin_clz(val); +} + +static int +big_sync_sched_set(struct ble_ll_iso_big *big, uint32_t offset_us) +{ + uint16_t max_ww_us; + uint16_t ww_us; + + big->sch.start_time = big->anchor_base_ticks; + big->sch.remainder = big->anchor_base_rem_us; + + ble_ll_tmr_add(&big->sch.start_time, &big->sch.remainder, offset_us); + + if (big->nse < 3) { + max_ww_us = (big->iso_interval * 1250 / 2) - BLE_LL_IFS; + } else { + max_ww_us = big->sub_interval; + } + + ww_us = ble_ll_utils_calc_window_widening(big->sch.start_time, + big->anchor_base_ticks, big->sca); + if (ww_us >= max_ww_us) { + return -1; + } + + ww_us += BLE_LL_JITTER_USECS; + BLE_LL_ASSERT(offset_us > ww_us); + + /* Reset anchor base before anchor offset wraps-around. + * This happens much earlier than the possible overflow in calculations. + */ + if (big->anchor_offset == UINT16_MAX) { + big->anchor_base_ticks = big->sch.start_time; + big->anchor_base_rem_us = big->sch.remainder; + big->anchor_offset = 0; + } + + big->sch.end_time = big->sch.start_time + ble_ll_tmr_u2t_up(big->sync_delay) + 1; + big->sch.start_time -= g_ble_ll_sched_offset_ticks; + + if (big->cstf) { + /* XXX calculate proper time */ + big->sch.end_time += 10; + } + + /* Adjust the start time to include window widening */ + ble_ll_tmr_sub(&big->sch.start_time, &big->sch.remainder, ww_us); + + /* Adjust end time to include window widening */ + big->sch.end_time += ble_ll_tmr_u2t_up(ww_us); + + big->wfr_us = big->tx_win_us + 2 * ww_us; + + return 0; +} + +static void +biginfo_func(struct ble_ll_sync_sm *syncsm, uint8_t sca, uint32_t sync_ticks, + uint8_t sync_rem_us, const uint8_t *data, uint8_t len, void *arg) +{ + struct ble_ll_iso_big_sync_params *sync_params = &g_ble_ll_iso_big_sync_params; + struct ble_ll_iso_params *iso_params; + struct ble_ll_iso_big *big; + struct ble_ll_iso_bis *bis; + uint64_t big_counter; + uint32_t big_offset; + uint8_t offset_unit; + uint32_t u32; + uint64_t u64; + uint8_t status; + uint8_t bis_cnt; + uint8_t big_phy; + uint32_t big_offset_us; + uint32_t offset_us; + uint16_t conn_handle; + bool encrypted; + int rc; + + big = sync_params->big; + BLE_LL_ASSERT(big); + + ble_ll_sync_biginfo_cb_set(sync_params->syncsm, NULL, NULL); + + encrypted = len == 57; + if (encrypted ^ !!big->encrypted) { + status = BLE_ERR_ENCRYPTION_MODE; + goto fail; + } + + u32 = get_le32(&data[0]); + big_offset = u32 & 0x3FFF; + offset_unit = u32 & 0x4000; + big->iso_interval = (u32 >> 15) & 0x0FFF; + + bis_cnt = (u32 >> 27) & 0x1F; + if (bis_cnt < __builtin_popcount(sync_params->bis_mask)) { + status = BLE_ERR_UNSUPPORTED; + goto fail; + } + + u32 = get_le32(&data[4]); + big->nse = u32 & 0x1F; + + if (big->mse == 0) { + /* The Controller can schedule reception of any number of subevents up to NSE. */ + big->mse = big->nse; + } + + big->bn = (u32 >> 5) & 0x07; + if (big->bn > big->mse) { + status = BLE_ERR_INV_HCI_CMD_PARMS; + goto fail; + } + + if (big->bn > MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC_MAX_BN)) { + status = BLE_ERR_UNSUPPORTED; + goto fail; + } + + big->sub_interval = (u32 >> 8) & 0x0FFFFF; + big->pto = (u32 >> 28) & 0x0F; + + u32 = get_le32(&data[8]); + big->bis_spacing = u32 & 0x0FFFFF; + big->irc = (u32 >> 20) & 0x0F; + big->max_pdu = (u32 >> 24) & 0xFF; + + big->framing_mode = (data[12] >> 5) & 0x80; + + uint32_t seed_aa; + seed_aa = get_le32(&data[13]); + (void)seed_aa; + + u32 = get_le32(&data[17]); + big->sdu_interval = u32 & 0x0FFFFF; + big->max_sdu = (u32 >> 20) & 0x0FFF; + + big->crc_init = get_le16(&data[21]); + memcpy(big->chan_map, &data[23], BLE_LL_CHAN_MAP_LEN); + big->chan_map[4] &= 0x1f; + big->chan_map_used = ble_ll_utils_chan_map_used_get(big->chan_map); + + big_phy = (data[27] >> 5) & 0x07; + if (ble_ll_iso_big_sync_phy_to_phy(big_phy, &big->phy) < 0) { + status = BLE_ERR_UNSUPPORTED; + goto fail; + } + + u64 = get_le64(&data[28]); + big->bis_counter = u64 & 0x7FFFFFFFFF; + big->framed = (u64 >> 39) & 0x01; + + big->big_counter = big->bis_counter / big->bn; + big->interleaved = big->sub_interval > big->bis_spacing; + big->ctrl_aa = ble_ll_utils_calc_big_aa(seed_aa, 0); + + u32 = sync_params->bis_mask; + + /* TODO: Fix duplicated data */ + iso_params = &big->params; + iso_params->iso_interval = big->iso_interval; + iso_params->sdu_interval = big->sdu_interval; + iso_params->max_sdu = big->max_sdu; + iso_params->max_pdu = big->max_pdu; + iso_params->bn = big->bn; + iso_params->pte = 0; + iso_params->framed = big->framed; + iso_params->framing_mode = big->framing_mode; + + STAILQ_INIT(&big->bis_q); + for (uint8_t bis_n = __builtin_ctz(u32); bis_n < bis_cnt; + bis_n = __builtin_ctz(u32)) { + bis = ble_ll_iso_big_alloc_bis(big); + if (bis == NULL) { + status = BLE_ERR_CONN_REJ_RESOURCES; + goto fail; + } + + bis->num = bis_n + 1; + bis->crc_init = (big->crc_init << 8) | (bis->num); + bis->aa = ble_ll_utils_calc_big_aa(seed_aa, bis->num); + bis->chan_id = bis->aa ^ (bis->aa >> 16); + + conn_handle = BLE_LL_CONN_HANDLE(BLE_LL_CONN_HANDLE_TYPE_BIS_SYNC, bis->num); + + ble_ll_iso_conn_init(&bis->conn, conn_handle, &bis->rx, NULL); + ble_ll_iso_rx_init(&bis->rx, &bis->conn, &big->params); + + u32 &= ~(1 << bis_n); + } + + /* Update the BIS indexes to the ones that we can to synchronize to */ + sync_params->bis_mask &= ~(u32); + + if (sync_params->bis_mask == 0) { + /* It looks like none of the requested BIS indexes have been found. */ + status = BLE_ERR_UNSUPPORTED; + goto fail; + } + + if (big->encrypted) { + memcpy(big->gskd, &data[41], 16); + ble_ll_iso_big_calculate_gsk(big, sync_params->broadcast_code); + + memcpy(big->giv, &data[33], 8); + ble_ll_iso_big_calculate_iv(big); + } + + big->cssn = CSSN_INITIAL; + + /* Core 5.3, Vol 6, Part B, 4.4.6.3 */ + big->mpt = ble_ll_pdu_us( + big->max_pdu + (big->encrypted ? 4 : 0), + ble_ll_phy_to_phy_mode(big->phy, BLE_HCI_LE_PHY_CODED_S8_PREF)); + + /* Core 5.3, Vol 6, Part B, 4.4.6.5 */ + big->sync_delay = msb_idx(sync_params->bis_mask) * big->bis_spacing + + (big->nse - 1) * big->sub_interval + big->mpt; + + big->sca = sca; + + /* Actual offset */ + big_offset_us = big_offset * (offset_unit ? 300 : 30); + /* Transmit window */ + big->tx_win_us = offset_unit ? 300 : 30; + + big->anchor_offset = 0; + big->anchor_base_ticks = sync_ticks; + big->anchor_base_rem_us = sync_rem_us; + + big_counter = big->big_counter; + while (true) { + offset_us = big->anchor_offset * big->iso_interval * 1250; + + rc = big_sync_sched_set(big, big_offset_us + offset_us); + if (rc < 0) { + status = BLE_ERR_UNSPECIFIED; + goto fail; + } + + if (LL_TMR_LEQ(big->sch.start_time, ble_ll_tmr_get())) { + rc = -1; + } else { + rc = ble_ll_sched_iso_big_sync(&big->sch); + } + if (rc >= 0) { + break; + } + + big->anchor_offset++; + } + + ble_ll_tmr_add(&big->anchor_base_ticks, &big->anchor_base_rem_us, big_offset_us + offset_us); + + big->bis_counter += (big_counter - big->big_counter) * big->bn; + big->big_counter = big_counter; + + return; +fail: + ble_ll_iso_big_sync_done(big, status); + ble_ll_iso_big_free(big); +} + +static void +ble_ll_iso_big_sync_event_done(struct ble_ll_iso_big *big) +{ + uint64_t big_counter; + uint32_t offset_us; + int rc; + + ble_ll_rfmgmt_release(); + + if (ble_ll_iso_big_term_pending(big)) { + goto term; + } + + big_counter = big->big_counter; + + while (true) { + big_counter++; + + if (big->control_active && big->control_instant == (uint16_t)big_counter) { + switch (big->control_active) { + case BIG_CONTROL_ACTIVE_TERM: + big->term_reason = BLE_ERR_REM_USER_CONN_TERM; + break; + case BIG_CONTROL_ACTIVE_CHAN_MAP: + big->chan_map_new_pending = true; + default: + break; + } + + big->control_active = 0; + big->cstf = 0; + } + + if (ble_ll_iso_big_term_pending(big)) { + goto term; + } + + if (big->chan_map_new_pending) { + big->chan_map_new_pending = false; + memcpy(big->chan_map, big->chan_map_new, BLE_LL_CHAN_MAP_LEN); + big->chan_map_used = ble_ll_utils_chan_map_used_get(big->chan_map); + } + + offset_us = (big->anchor_offset + 1) * big->iso_interval * 1250; + + rc = big_sync_sched_set(big, offset_us); + if (rc < 0) { + big->term_reason = BLE_ERR_CONN_SPVN_TMO; + goto term; + } + + rc = ble_ll_sched_iso_big_sync(&big->sch); + if (rc >= 0) { + break; + } + + big->anchor_offset++; + } + + big->bis_counter += (big_counter - big->big_counter) * big->bn; + big->big_counter = big_counter; + + return; +term: + if (ble_ll_iso_big_sync_pending(big)) { + ble_ll_iso_big_sync_done(big, big->term_reason); + } else if (big->term_reason != BLE_ERR_CONN_TERM_LOCAL) { + ble_ll_iso_big_sync_hci_evt_lost(big); + } + ble_ll_iso_big_free(big); +} + +static void +ble_ll_iso_big_sync_event_done_ev(struct ble_npl_event *ev) +{ + struct ble_ll_iso_big *big; + struct ble_ll_iso_bis *bis; + + big = CONTAINER_OF(ev, struct ble_ll_iso_big, event_done); + + STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { + ble_ll_iso_rx_event_done(&bis->rx); + } + + ble_ll_iso_big_sync_event_done(big); +} + +static void +ble_ll_iso_big_sync_event_done_to_ll(struct ble_ll_iso_big *big) +{ + ble_ll_event_add(&big->event_done); +} + +void +ble_ll_iso_big_sync_halt(void) +{ + ble_phy_disable(); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + + if (g_ble_ll_iso_big_curr) { + ble_ll_iso_big_sync_event_done_to_ll(g_ble_ll_iso_big_curr); + g_ble_ll_iso_big_curr = NULL; + } +} + +static uint8_t +pdu_idx_get(struct ble_ll_iso_big *big, struct ble_ll_iso_bis *bis) +{ + /* Core 5.3, Vol 6, Part B, 4.4.6.6 */ + if (bis->g < big->irc) { + return bis->n; + } + + /* Pretransmission */ + return big->bn * big->pto * (bis->g - big->irc + 1) + bis->n; +} + +static int +ble_ll_iso_big_sync_bis_subevent_rx(struct ble_ll_iso_big *big) +{ + struct ble_ll_iso_bis *bis; + uint16_t chan_idx; + int rc; + + bis = big->bis; + + if (bis->subevent_num == 0) { + chan_idx = ble_ll_utils_dci_iso_event(big->big_counter, bis->chan_id, + &bis->prn_sub_lu, big->chan_map_used, + big->chan_map, &bis->remap_idx); + } else { + chan_idx = ble_ll_utils_dci_iso_subevent(bis->chan_id, &bis->prn_sub_lu, + big->chan_map_used, + big->chan_map, &bis->remap_idx); + } + + bis->chan_idx = chan_idx; + + rc = ble_phy_setchan(chan_idx, bis->aa, bis->crc_init); + if (rc != 0) { + return rc; + } + + if (big->encrypted) { + ble_phy_encrypt_enable(big->gsk); + ble_phy_encrypt_header_mask_set(BLE_LL_PDU_HEADERMASK_BIS); + ble_phy_encrypt_iv_set(bis->iv); + ble_phy_encrypt_counter_set(big->bis_counter + pdu_idx_get(big, bis), 1); + } else { + ble_phy_encrypt_disable(); + } + + return 0; +} + +static int +ble_ll_iso_big_sync_ctrl_subevent_rx(struct ble_ll_iso_big *big) +{ + uint16_t chan_idx; + uint16_t chan_id; + uint16_t foo, bar; + int rc; + + chan_id = big->ctrl_aa ^ (big->ctrl_aa >> 16); + + chan_idx = ble_ll_utils_dci_iso_event(big->big_counter, chan_id, &foo, + big->chan_map_used, big->chan_map, &bar); + + rc = ble_phy_setchan(chan_idx, big->ctrl_aa, big->crc_init << 8); + if (rc != 0) { + return rc; + } + + if (big->encrypted) { + ble_phy_encrypt_enable(big->gsk); + ble_phy_encrypt_header_mask_set(BLE_LL_PDU_HEADERMASK_BIS); + ble_phy_encrypt_iv_set(big->iv); + ble_phy_encrypt_counter_set(big->bis_counter, 1); + } else { + ble_phy_encrypt_disable(); + } + + return 0; +} + +static int +ble_ll_iso_big_sync_rx_start(struct ble_ll_iso_big *big, uint32_t start_time, + uint32_t remainder) +{ + int rc; + + rc = ble_phy_rx_set_start_time(start_time, remainder); + if (rc != 0 && rc != BLE_PHY_ERR_RX_LATE) { + return rc; + } + + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, big->wfr_us); + + return 0; +} + +static int +ble_ll_iso_big_sync_event_sched_cb(struct ble_ll_sched_item *sch) +{ + struct ble_ll_iso_big *big = sch->cb_arg; + struct ble_ll_iso_bis *bis; + uint32_t timestamp; +#if MYNEWT_VAL(BLE_LL_PHY) + uint8_t phy_mode; +#endif + bool to_rx; + int rc; + + BLE_LL_ASSERT(big); + + ble_ll_state_set(BLE_LL_STATE_BIG_SYNC); + g_ble_ll_iso_big_curr = big; + + ble_ll_whitelist_disable(); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + ble_phy_resolv_list_disable(); +#endif +#if MYNEWT_VAL(BLE_LL_PHY) + phy_mode = ble_ll_phy_to_phy_mode(big->phy, 0); + ble_phy_mode_set(phy_mode, phy_mode); +#endif + + ble_ll_tx_power_set(g_ble_ll_tx_power); + + timestamp = ble_ll_tmr_t2u(big->anchor_base_ticks) + big->anchor_base_rem_us; + if (big->framed) { + timestamp += big->sync_delay + big->sdu_interval + big->iso_interval * 1250; + } else { + timestamp += big->sync_delay; + } + timestamp += big->anchor_offset * big->iso_interval * 1250; + + /* XXX calculate this in advance at the end of a previous event? */ + big->subevents_rem = big->num_bis * big->nse * big->bn; + STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { + ble_ll_iso_rx_event_start(&bis->rx, timestamp); + + bis->subevent_num = 0; + bis->n = 0; + bis->g = 0; + } + + /* Select 1st BIS for reception */ + big->bis = STAILQ_FIRST(&big->bis_q); + + rc = ble_ll_iso_big_sync_bis_subevent_rx(big); + if (rc != 0) { + goto done; + } + + to_rx = big->subevents_rem > 1; + ble_phy_transition_set( + to_rx ? BLE_PHY_TRANSITION_TO_RX_ISO_SUBEVENT : BLE_PHY_TRANSITION_NONE, + big->interleaved ? big->bis_spacing : big->sub_interval); + + rc = ble_ll_iso_big_sync_rx_start( + big, sch->start_time + g_ble_ll_sched_offset_ticks, sch->remainder); + if (rc != 0) { + goto done; + } + + return BLE_LL_SCHED_STATE_RUNNING; +done: + ble_ll_iso_big_sync_halt(); + return BLE_LL_SCHED_STATE_DONE; +} + +static int +ble_ll_iso_big_sync_create(uint8_t big_handle, struct ble_ll_iso_big **out) +{ + struct ble_ll_iso_big *big; + + big = big_ll_iso_big_find(big_handle); + if (big) { + return -EALREADY; + } + + big = ble_ll_iso_big_alloc(big_handle); + if (!big) { + return -ENOMEM; + } + + big->sch.sched_type = BLE_LL_SCHED_TYPE_BIG_SYNC; + big->sch.sched_cb = ble_ll_iso_big_sync_event_sched_cb; + big->sch.cb_arg = big; + ble_npl_event_init(&big->event_done, ble_ll_iso_big_sync_event_done_ev, NULL); + + *out = big; + + return 0; +} + +static int +ble_ll_big_sync_terminate(uint8_t big_handle) +{ + struct ble_ll_iso_big *big; + + big = big_ll_iso_big_find(big_handle); + if (big == NULL) { + return -ENOENT; + } + + big->term_reason = BLE_ERR_CONN_TERM_LOCAL; + + if (big == g_ble_ll_iso_big_curr) { + /* Terminate the BIG once BIG event is complete */ + return 0; + } + + if (ble_ll_iso_big_sync_pending(big)) { + ble_ll_iso_big_sync_done(big, BLE_ERR_CONN_TERM_LOCAL); + } + + ble_ll_iso_big_free(big); + + return 0; +} + +int +ble_ll_iso_big_sync_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *rxhdr) +{ + struct ble_ll_iso_big *big; + + big = g_ble_ll_iso_big_curr; + BLE_LL_ASSERT(big); + + if (big->subevents_rem == big->num_bis * big->nse * big->bn) { + big->anchor_offset = 0; + big->anchor_base_ticks = rxhdr->beg_cputime; + big->anchor_base_rem_us = rxhdr->rem_usecs; + } + + return ble_ll_iso_big_term_pending(big) ? -1 : 0; +} + +static int +ble_ll_iso_big_sync_subevent_done(struct ble_ll_iso_big *big) +{ + struct ble_ll_iso_bis *bis; + + if (big->subevents_rem > 0) { + bis = big->bis; + + bis->n++; + if (bis->n == big->bn) { + bis->n = 0; + bis->g++; + } + + bis->subevent_num++; + + /* Switch to next BIS if interleaved or all subevents for current BIS were transmitted. */ + if (big->interleaved || (bis->subevent_num == big->nse)) { + bis = STAILQ_NEXT(bis, bis_q_next); + if (!bis) { + bis = STAILQ_FIRST(&big->bis_q); + } + big->bis = bis; + } + + big->subevents_rem--; + if (big->subevents_rem > 0) { + return ble_ll_iso_big_sync_bis_subevent_rx(big); + } + + if (big->cstf) { + return ble_ll_iso_big_sync_ctrl_subevent_rx(big); + } + } else if (big->cstf) { + big->cstf = false; + } else { + BLE_LL_ASSERT(0); + } + + return -1; +} + +static int +ble_ll_iso_big_sync_data_pdu_in(uint16_t conn_handle, uint8_t idx, struct os_mbuf *rxpdu) +{ + struct ble_ll_iso_conn *conn; + + conn = ble_ll_iso_conn_find_by_handle(conn_handle); + if (conn == NULL) { + os_mbuf_free_chain(rxpdu); + return 0; + } + + BLE_LL_ASSERT(conn->rx); + + return ble_ll_iso_rx_pdu_put(conn->rx, idx, rxpdu); +} + +static void +ble_ll_iso_big_sync_chan_map_ind(struct ble_ll_iso_big *big, struct os_mbuf *rxpdu) +{ + struct ble_ll_big_ctrl_chan_map_ind *ind; + + rxpdu = os_mbuf_pullup(rxpdu, sizeof(*ind)); + if (rxpdu == NULL) { + return; + } + + ind = (void *)rxpdu->om_data; + + big->control_active = BIG_CONTROL_ACTIVE_CHAN_MAP; + big->control_instant = le16toh(ind->instant); + memcpy(big->chan_map_new, ind->chan_map, sizeof(big->chan_map_new)); +} + +static void +ble_ll_iso_big_sync_term_ind(struct ble_ll_iso_big *big, struct os_mbuf *rxpdu) +{ + struct ble_ll_big_ctrl_term_ind *ind; + + rxpdu = os_mbuf_pullup(rxpdu, sizeof(*ind)); + if (rxpdu == NULL) { + return; + } + + ind = (void *)rxpdu->om_data; + + big->control_active = BIG_CONTROL_ACTIVE_TERM, + big->control_instant = le16toh(ind->instant); +} + +static void +ble_ll_iso_big_sync_ctrl_pdu_in(struct ble_ll_iso_big *big, struct os_mbuf *rxpdu) +{ + uint8_t *rxbuf; + uint8_t opcode; + + if (os_mbuf_len(rxpdu) == 0) { + return; + } + + rxpdu = os_mbuf_pullup(rxpdu, sizeof(opcode)); + rxbuf = rxpdu->om_data; + + opcode = rxbuf[0]; + os_mbuf_adj(rxpdu, sizeof(opcode)); + + switch (opcode) { + case BLE_LL_BIG_CTRL_CHAN_MAP_IND: + ble_ll_iso_big_sync_chan_map_ind(big, rxpdu); + break; + case BLE_LL_BIG_CTRL_TERM_IND: + ble_ll_iso_big_sync_term_ind(big, rxpdu); + break; + } + + STATS_INC(ble_ll_iso_big_sync_stats, rx_ctrl_pdus); +} + +int +ble_ll_iso_big_sync_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) +{ + int rc = 0; + struct ble_ll_iso_big *big; + uint8_t hdr_byte; + uint8_t rx_pyld_len; + struct os_mbuf *rxpdu = NULL; + bool alloc_rxpdu; + bool to_rx; + + big = g_ble_ll_iso_big_curr; + BLE_LL_ASSERT(big); + + hdr_byte = rxbuf[0]; + rx_pyld_len = rxbuf[1]; + + /** + * No need to alloc rxpdu for BIG Control PDU with invalid CRC. + * ISO Data PDUs with CRC errors shall be reported as per Core 6.0 Vol 6, Part G. + */ + alloc_rxpdu = BLE_MBUF_HDR_CRC_OK(rxhdr); + if (alloc_rxpdu) { + /* Allocate buffer to copy the Broadcast Isochronous PDU header and payload */ + rxpdu = ble_ll_rxpdu_alloc(BLE_LL_PDU_HDR_LEN + rx_pyld_len); + /* TODO: Remove the assert below */ + BLE_LL_ASSERT(rxpdu); + } + + if (rxpdu) { + ble_phy_rxpdu_copy(rxbuf, rxpdu); + + if (BLE_LL_BIS_LLID_IS_DATA(hdr_byte)) { + /* Copy the packet header */ + memcpy(BLE_MBUF_HDR_PTR(rxpdu), rxhdr, sizeof(struct ble_mbuf_hdr)); + } + + ble_ll_rx_pdu_in(rxpdu); + } + + to_rx = big->subevents_rem > 0 || big->cstf; + ble_phy_transition_set( + to_rx ? BLE_PHY_TRANSITION_TO_RX_ISO_SUBEVENT : BLE_PHY_TRANSITION_NONE, + big->interleaved ? big->bis_spacing : big->sub_interval); + + if (!to_rx) { + ble_ll_iso_big_sync_halt(); + rc = -1; + } + + return ble_ll_iso_big_term_pending(big) ? -1 : rc; +} + +int +ble_ll_iso_big_sync_rx_isr_early_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) +{ + union iso_pdu_user_data pdu_user_data = { 0 }; + struct ble_ll_iso_big *big; + struct ble_ll_iso_bis *bis; + uint8_t hdr_byte; + uint8_t cssn; + uint8_t cstf; + bool crc_ok; + + big = g_ble_ll_iso_big_curr; + BLE_LL_ASSERT(big); + + crc_ok = BLE_MBUF_HDR_CRC_OK(rxhdr); + + if (crc_ok) { + hdr_byte = rxbuf[0]; + + /* Control Subevent Sequence Number */ + cssn = BLE_LL_BIS_PDU_HDR_CSSN(hdr_byte); + + /* Control Subevent Transmission Flag */ + cstf = BLE_LL_BIS_PDU_HDR_CSTF(hdr_byte); + + if (cstf > 0 && cssn != big->cssn) { + big->cstf = cstf; + big->cssn = cssn; + } + + if (BLE_LL_BIS_LLID_IS_DATA(hdr_byte)) { + bis = big->bis; + + /* Save the BIS connection handle and PDU index */ + rxhdr->rxinfo.handle = bis->conn.handle; + pdu_user_data.pdu_idx = pdu_idx_get(big, bis); + } + + pdu_user_data.big_handle = big->handle; + rxhdr->rxinfo.user_data = UINT_TO_POINTER(pdu_user_data.value); + } + + ble_ll_iso_big_sync_subevent_done(big); + + return 0; +} + +void +ble_ll_iso_big_sync_rx_pdu_in(struct os_mbuf **rxpdu, struct ble_mbuf_hdr *rxhdr) +{ + union iso_pdu_user_data pdu_user_data = { 0 }; + struct ble_ll_iso_big *big; + uint8_t *rxbuf; + uint8_t hdr_byte; + uint8_t rx_pyld_len; + + BLE_LL_ASSERT(BLE_MBUF_HDR_CRC_OK(rxhdr)); + + pdu_user_data.value = POINTER_TO_UINT(rxhdr->rxinfo.user_data); + + big = big_ll_iso_big_find(pdu_user_data.big_handle); + if (big == NULL) { + STATS_INC(ble_ll_iso_big_sync_stats, no_big); + /* rxpdu will be free'd */ + return; + } + + if (ble_ll_iso_big_sync_pending(big)) { + ble_ll_iso_big_sync_done(big, BLE_ERR_SUCCESS); + } + + /* Validate rx data pdu */ + rxbuf = (*rxpdu)->om_data; + hdr_byte = rxbuf[0]; + rx_pyld_len = rxbuf[1]; + + if (BLE_LL_BIS_LLID_IS_CTRL(hdr_byte)) { + os_mbuf_adj(*rxpdu, BLE_LL_PDU_HDR_LEN); + BLE_LL_ASSERT(rx_pyld_len == os_mbuf_len(*rxpdu)); + ble_ll_iso_big_sync_ctrl_pdu_in(big, *rxpdu); + } else if (BLE_LL_BIS_LLID_IS_DATA(hdr_byte)) { +#if 0 + const uint8_t *dptr; + uint32_t big_counter; + uint32_t bis_counter; + uint8_t subevent_num; + uint8_t bis_num; + uint8_t g; + uint8_t n; + bool mismatch; + + dptr = &rxbuf[2]; + big_counter = get_be32(dptr); + bis_counter = get_be32(&dptr[4]); + subevent_num = dptr[8]; + g = dptr[9]; + n = dptr[10]; + bis_num = dptr[13] >> 4; + mismatch = (big_counter != (uint32_t)big->big_counter) || (bis_counter != (uint32_t)big->bis_counter); + + (void)subevent_num; + (void)g; + (void)n; + (void)bis_num; + + ble_ll_hci_ev_send_vs_printf(dptr[12], "%d big (%d vs %d), bis (%d vs %d) %s", + pdu_user_data.pdu_idx, big_counter, (uint32_t)big->big_counter, bis_counter, + (uint32_t)big->bis_counter, mismatch ? "MISMATCH" : ""); + + (void)ble_ll_iso_big_sync_data_pdu_in; +#else + ble_ll_iso_big_sync_data_pdu_in(rxhdr->rxinfo.handle, + pdu_user_data.pdu_idx, *rxpdu); + + /* Do not free the buffer */ + *rxpdu = NULL; +#endif + } +} + +static int +ble_ll_iso_big_sync_rx_restart(struct ble_ll_iso_big *big) +{ + struct ble_ll_iso_bis *bis; + uint32_t offset_us; + uint32_t ticks; + uint16_t interval_us; + uint8_t rem_us; + bool to_rx; + + interval_us = big->interleaved ? big->bis_spacing : big->sub_interval; + ticks = big->sch.start_time; + rem_us = big->sch.remainder; + + offset_us = 0; + STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { + offset_us += bis->subevent_num * interval_us; + } + ble_ll_tmr_add(&ticks, &rem_us, offset_us); + + to_rx = big->subevents_rem > 0 || big->cstf; + ble_phy_transition_set(to_rx ? BLE_PHY_TRANSITION_TO_RX_ISO_SUBEVENT + : BLE_PHY_TRANSITION_NONE, + interval_us); + + return ble_ll_iso_big_sync_rx_start(big, ticks + g_ble_ll_sched_offset_ticks, + rem_us); +} + +static bool +ble_ll_iso_big_sync_is_lost(struct ble_ll_iso_big *big) +{ + uint32_t ticks; + uint8_t rem_us; + + ticks = big->anchor_base_ticks; + rem_us = big->anchor_base_rem_us; + + ble_ll_tmr_add(&ticks, &rem_us, BIG_SYNC_TIMEOUT_US(big->sync_timeout)); + + return LL_TMR_LEQ(ticks, ble_ll_tmr_get()); +} + +void +ble_ll_iso_big_sync_wfr_timer_exp(void) +{ + struct ble_ll_iso_big *big = g_ble_ll_iso_big_curr; + int rc; + + STATS_INC(ble_ll_iso_big_sync_stats, wfr_expirations); + + ble_phy_disable(); + + if (big == NULL) { + return; + } + + if (big->subevents_rem == big->num_bis * big->nse * big->bn) { + big->anchor_offset++; + } + + if (ble_ll_iso_big_term_pending(big)) { + goto event_done; + } + + if (ble_ll_iso_big_sync_is_lost(big)) { + big->term_reason = BLE_ERR_CONN_SPVN_TMO; + goto event_done; + } + + /* PDU not received. Mark the subevent as done, prepare for the next RX if needed. */ + rc = ble_ll_iso_big_sync_subevent_done(big); + if (rc != 0) { + goto event_done; + } + + /* Restart RX. Start time shall be adjusted to the next expected subevent. */ + rc = ble_ll_iso_big_sync_rx_restart(big); + if (rc != 0) { + goto event_done; + } + + return; +event_done: + ble_ll_iso_big_sync_halt(); +} + +int +ble_ll_iso_big_sync_hci_create(const uint8_t *cmdbuf, uint8_t len) +{ + struct ble_ll_iso_big_sync_params *sync_params = &g_ble_ll_iso_big_sync_params; + const struct ble_hci_le_big_create_sync_cp *cmd = (void *)cmdbuf; + struct ble_ll_sync_sm *syncsm; + struct ble_ll_iso_big *big; + uint16_t sync_timeout; + uint16_t sync_handle; + uint32_t bis_mask; + int rc; + + if (len != sizeof(*cmd) + cmd->num_bis * sizeof(cmd->bis[0])) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (sync_params->big) { + return BLE_ERR_CMD_DISALLOWED; + } + + sync_timeout = le16toh(cmd->sync_timeout); + sync_handle = le16toh(cmd->sync_handle); + + if (!IN_RANGE(cmd->big_handle, 0x00, 0xef) || + !IN_RANGE(sync_handle, 0x00, 0x0eff) || !IN_RANGE(cmd->mse, 0x00, 0x1f) || + !IN_RANGE(sync_timeout, 0x000a, 0x4000) || + !IN_RANGE(cmd->num_bis, 0x01, 0x1f) || (cmd->encryption) > 1) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->num_bis > MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC_MAX_BIS)) { + return BLE_ERR_CONN_REJ_RESOURCES; + } + + syncsm = ble_ll_sync_get(sync_handle); + if (!syncsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + bis_mask = 0; + for (int i = 0; i < cmd->num_bis; i++) { + uint8_t bis = cmd->bis[i]; + if (!IN_RANGE(bis, 0x01, 0x1f)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + bis_mask |= 1 << (bis - 1); + } + + rc = ble_ll_iso_big_sync_create(cmd->big_handle, &big); + if (rc == -EALREADY) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (rc == -ENOMEM) { + return BLE_ERR_CONN_REJ_RESOURCES; + } + + if (big == NULL) { + return BLE_ERR_UNSPECIFIED; + } + + memcpy(sync_params->broadcast_code, cmd->broadcast_code, + sizeof(sync_params->broadcast_code)); + sync_params->big = big; + sync_params->syncsm = syncsm; + sync_params->bis_mask = bis_mask; + + big->sync_timeout = sync_timeout; + big->encrypted = cmd->encryption ? 1 : 0; + big->mse = cmd->mse; + + ble_ll_sync_biginfo_cb_set(sync_params->syncsm, biginfo_func, NULL); + + return 0; +} + +int +ble_ll_iso_big_sync_hci_terminate(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_big_terminate_sync_cp *cmd = (void *)cmdbuf; + struct ble_hci_le_big_terminate_sync_rp *rsp = (void *)rspbuf; + int err; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + rsp->big_handle = cmd->big_handle; + *rsplen = sizeof(*rsp); + + err = ble_ll_big_sync_terminate(cmd->big_handle); + switch (err) { + case 0: + break; + case -ENOENT: + return BLE_ERR_UNK_ADV_INDENT; + default: + return BLE_ERR_UNSPECIFIED; + } + + return 0; +} + +void +ble_ll_iso_big_sync_init(void) +{ + int rc; + + /* Register ISO BIG sync statistics */ + rc = stats_init_and_reg( + STATS_HDR(ble_ll_iso_big_sync_stats), + STATS_SIZE_INIT_PARMS(ble_ll_iso_big_sync_stats, STATS_SIZE_32), + STATS_NAME_INIT_PARMS(ble_ll_iso_big_sync_stats), "ble_ll_iso_big_sync"); + BLE_LL_ASSERT(rc == 0); + + rc = os_mempool_init(&mp_big, BIG_POOL_SIZE, sizeof(struct ble_ll_iso_big), + mb_big, "sbig"); + BLE_LL_ASSERT(rc == 0); + rc = os_mempool_init(&mp_bis, BIS_POOL_SIZE, sizeof(struct ble_ll_iso_bis), + mb_bis, "sbis"); + BLE_LL_ASSERT(rc == 0); + + STAILQ_INIT(&big_q); +} + +void +ble_ll_iso_big_sync_reset(void) +{ + struct ble_ll_iso_big *big; + + /* Reset statistics */ + STATS_RESET(ble_ll_iso_big_sync_stats); + + big = STAILQ_FIRST(&big_q); + while (big) { + ble_ll_iso_big_free(big); + big = STAILQ_FIRST(&big_q); + } + + memset(&g_ble_ll_iso_big_sync_params, 0, sizeof(g_ble_ll_iso_big_sync_params)); + g_ble_ll_iso_big_curr = NULL; +} + +#endif /* BLE_LL_ISO_BROADCAST_SYNC */ diff --git a/nimble/controller/src/ble_ll_iso_priv.h b/nimble/controller/src/ble_ll_iso_priv.h new file mode 100644 index 0000000000..260660ef8f --- /dev/null +++ b/nimble/controller/src/ble_ll_iso_priv.h @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_ISO_PRIV_ +#define H_BLE_LL_ISO_PRIV_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ISO Parameters */ +struct ble_ll_iso_params { + /* SDU Interval */ + uint32_t sdu_interval; + /* ISO Interval */ + uint16_t iso_interval; + /* Maximum SDU size */ + uint16_t max_sdu; + /* Max PDU length */ + uint8_t max_pdu; + /* Burst Number */ + uint8_t bn; + /* Pre-transmission */ + uint8_t pte; + /* Framing */ + uint8_t framed : 1; + /* Framing mode */ + uint8_t framing_mode : 1; +}; + +/** + * @struct ble_ll_iso_data_path_cb + * + * Interface structure for ISO data path callbacks. + */ +struct ble_ll_iso_data_path_cb { + /** + * @brief Callback function for ISO SDU (Service Data Unit) output. + * + * @param conn_handle The connection handle associated with the received SDU. + * @param om Pointer to the `os_mbuf` structure containing the SDU data. + * Can be `NULL` if the SDU is considered as lost. + * @param timestamp Timestamp associated with the received SDU. + * @param seq_num Sequence number of the SDU. + * @param valid Status of the SDU reception. + * - `true`: SDU was received successfully, and `om` contains valid data. + * - `false`: An error occurred during processing, but partial or corrupted + * SDU data may be available in `om`. + */ + void (*sdu_out)(uint16_t conn_handle, const struct os_mbuf *om, + uint32_t timestamp, uint16_t seq_num, bool valid); +}; + +/* Forward declaration */ +struct ble_ll_iso_conn; + +/* ISO Rx object */ +struct ble_ll_iso_rx { + struct { + uint8_t payload_type; + uint32_t received_sdu_count; + uint32_t missed_sdu_count; + uint32_t failed_sdu_count; + } test; + + /* ISOAL Demultiplexer */ + struct ble_ll_isoal_demux demux; + + /* ISO Connection */ + struct ble_ll_iso_conn *conn; + + /* ISO Parameters */ + const struct ble_ll_iso_params *params; + + /* ISO Data Path */ + const struct ble_ll_iso_data_path_cb *data_path; +}; + +/* ISO Tx object */ +struct ble_ll_iso_tx { + struct { + uint8_t payload_type; + uint32_t rand; + } test; + + /* ISOAL Multiplexer */ + struct ble_ll_isoal_mux mux; + + /* ISO Connection */ + struct ble_ll_iso_conn *conn; + + /* ISO Parameters */ + const struct ble_ll_iso_params *params; + + /* ISO Data Path */ + const struct ble_ll_iso_data_path_cb *data_path; +}; + +/* ISO Connection object */ +struct ble_ll_iso_conn { + /* ISO Rx Context */ + struct ble_ll_iso_rx *rx; + + /* ISO Tx Context */ + struct ble_ll_iso_tx *tx; + + /* Connection handle */ + uint16_t handle; + + /* HCI SDU Fragment */ + struct os_mbuf *frag; + + /* Number of Completed Packets */ + uint16_t num_completed_pkt; + + STAILQ_ENTRY(ble_ll_iso_conn) iso_conn_q_next; +}; + +void ble_ll_iso_conn_init(struct ble_ll_iso_conn *conn, uint16_t conn_handle, + struct ble_ll_iso_rx *rx, struct ble_ll_iso_tx *tx); +void ble_ll_iso_conn_reset(struct ble_ll_iso_conn *conn); + +void ble_ll_iso_tx_init(struct ble_ll_iso_tx *tx, struct ble_ll_iso_conn *conn, + const struct ble_ll_iso_params *params); +void ble_ll_iso_tx_reset(struct ble_ll_iso_tx *tx); +int ble_ll_iso_tx_event_start(struct ble_ll_iso_tx *tx, uint32_t timestamp); +int ble_ll_iso_tx_event_done(struct ble_ll_iso_tx *tx); +int ble_ll_iso_tx_pdu_get(struct ble_ll_iso_tx *tx, uint8_t idx, + uint32_t pkt_counter, uint8_t *llid, void *dptr); + +void ble_ll_iso_rx_init(struct ble_ll_iso_rx *rx, struct ble_ll_iso_conn *conn, + const struct ble_ll_iso_params *params); +void ble_ll_iso_rx_reset(struct ble_ll_iso_rx *rx); +int ble_ll_iso_rx_event_start(struct ble_ll_iso_rx *rx, uint32_t timestamp); +int ble_ll_iso_rx_event_done(struct ble_ll_iso_rx *rx); +int ble_ll_iso_rx_pdu_put(struct ble_ll_iso_rx *rx, uint8_t idx, struct os_mbuf *om); + +struct ble_ll_iso_conn *ble_ll_iso_conn_find_by_handle(uint16_t conn_handle); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/nimble/controller/src/ble_ll_isoal.c b/nimble/controller/src/ble_ll_isoal.c index 2cb842aa4f..506e0105a9 100644 --- a/nimble/controller/src/ble_ll_isoal.c +++ b/nimble/controller/src/ble_ll_isoal.c @@ -17,47 +17,38 @@ * under the License. */ -#include -#include +#include "ble_ll_iso_priv.h" #include #include +#include +#include #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif #if MYNEWT_VAL(BLE_LL_ISO) - void -ble_ll_isoal_mux_init(struct ble_ll_isoal_mux *mux, uint8_t max_pdu, - uint32_t iso_interval_us, uint32_t sdu_interval_us, - uint8_t bn, uint8_t pte, bool framed, uint8_t framing_mode) +ble_ll_isoal_mux_init(struct ble_ll_isoal_mux *mux, struct ble_ll_isoal_config *config) { memset(mux, 0, sizeof(*mux)); + mux->config = *config; - mux->max_pdu = max_pdu; /* Core 5.3, Vol 6, Part G, 2.1 */ - mux->sdu_per_interval = iso_interval_us / sdu_interval_us; + mux->sdu_per_interval = config->iso_interval_us / config->sdu_interval_us; - if (framed) { - /* TODO */ - } else { - mux->pdu_per_sdu = bn / mux->sdu_per_interval; + if (!config->framed) { + mux->pdu_per_sdu = config->bn / mux->sdu_per_interval; } - mux->sdu_per_event = (1 + pte) * mux->sdu_per_interval; - - mux->bn = bn; + mux->sdu_per_event = (1 + config->pte) * mux->sdu_per_interval; STAILQ_INIT(&mux->sdu_q); mux->sdu_q_len = 0; - - mux->framed = framed; - mux->framing_mode = framing_mode; } void -ble_ll_isoal_mux_free(struct ble_ll_isoal_mux *mux) +ble_ll_isoal_mux_reset(struct ble_ll_isoal_mux *mux) { struct os_mbuf_pkthdr *pkthdr; struct os_mbuf *om; @@ -81,7 +72,7 @@ ble_ll_isoal_mux_free(struct ble_ll_isoal_mux *mux) } void -ble_ll_isoal_mux_sdu_enqueue(struct ble_ll_isoal_mux *mux, struct os_mbuf *om) +ble_ll_isoal_mux_sdu_put(struct ble_ll_isoal_mux *mux, struct os_mbuf *om) { struct os_mbuf_pkthdr *pkthdr; os_sr_t sr; @@ -118,12 +109,13 @@ ble_ll_isoal_mux_event_start(struct ble_ll_isoal_mux *mux, uint32_t timestamp) mux->sdu_in_event = 0; } #else - if (mux->framed) { + if (mux->config.framed) { mux->sdu_in_event = mux->sdu_q_len; } else { mux->sdu_in_event = min(mux->sdu_q_len, mux->sdu_per_event); } #endif + mux->event_tx_timestamp = timestamp; return mux->sdu_in_event; @@ -182,6 +174,7 @@ ble_ll_isoal_mux_unframed_event_done(struct ble_ll_isoal_mux *mux) static int ble_ll_isoal_mux_framed_event_done(struct ble_ll_isoal_mux *mux) { + const struct ble_ll_isoal_config *config = &mux->config; struct os_mbuf_pkthdr *pkthdr; struct os_mbuf *om; struct os_mbuf *om_next; @@ -200,7 +193,7 @@ ble_ll_isoal_mux_framed_event_done(struct ble_ll_isoal_mux *mux) return 0; } - num_pdu = mux->bn; + num_pdu = config->bn; #if MYNEWT_VAL(BLE_LL_ISO_HCI_DISCARD_THRESHOLD) /* Drop queued SDUs if number of queued SDUs exceeds defined threshold. @@ -211,8 +204,7 @@ ble_ll_isoal_mux_framed_event_done(struct ble_ll_isoal_mux *mux) */ uint32_t thr = MYNEWT_VAL(BLE_LL_ISO_HCI_DISCARD_THRESHOLD); if (mux->sdu_q_len > mux->sdu_per_event + thr * mux->sdu_per_interval) { - num_sdu = mux->sdu_q_len - mux->sdu_per_event - - thr * mux->sdu_per_interval; + num_sdu = mux->sdu_q_len - isoal->sdu_per_event - thr * mux->sdu_per_interval; } #endif @@ -226,14 +218,14 @@ ble_ll_isoal_mux_framed_event_done(struct ble_ll_isoal_mux *mux) hdr_len = sc ? 2 /* Segmentation Header */ : 5 /* Segmentation Header + TimeOffset */; - if (mux->max_pdu <= hdr_len + pdu_offset) { + if (config->max_pdu <= hdr_len + pdu_offset) { /* Advance to next PDU */ pdu_offset = 0; num_pdu--; continue; } - frag_len = min(rem_len, mux->max_pdu - hdr_len - pdu_offset); + frag_len = min(rem_len, config->max_pdu - hdr_len - pdu_offset); pdu_offset += hdr_len + frag_len; @@ -269,32 +261,11 @@ ble_ll_isoal_mux_framed_event_done(struct ble_ll_isoal_mux *mux) return pkt_freed; } -int -ble_ll_isoal_mux_event_done(struct ble_ll_isoal_mux *mux) -{ - struct os_mbuf_pkthdr *pkthdr; - struct ble_mbuf_hdr *blehdr; - struct os_mbuf *om; - - pkthdr = STAILQ_FIRST(&mux->sdu_q); - if (pkthdr) { - om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); - blehdr = BLE_MBUF_HDR_PTR(om); - mux->last_tx_timestamp = mux->event_tx_timestamp; - mux->last_tx_packet_seq_num = blehdr->txiso.packet_seq_num; - } - - if (mux->framed) { - return ble_ll_isoal_mux_framed_event_done(mux); - } - - return ble_ll_isoal_mux_unframed_event_done(mux); -} - static int ble_ll_isoal_mux_unframed_get(struct ble_ll_isoal_mux *mux, uint8_t idx, uint8_t *llid, void *dptr) { + const struct ble_ll_isoal_config *config = &mux->config; struct os_mbuf_pkthdr *pkthdr; struct os_mbuf *om; int32_t rem_len; @@ -322,7 +293,7 @@ ble_ll_isoal_mux_unframed_get(struct ble_ll_isoal_mux *mux, uint8_t idx, } om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); - sdu_offset = pdu_idx * mux->max_pdu; + sdu_offset = pdu_idx * config->max_pdu; rem_len = OS_MBUF_PKTLEN(om) - sdu_offset; if (OS_MBUF_PKTLEN(om) == 0) { @@ -339,8 +310,8 @@ ble_ll_isoal_mux_unframed_get(struct ble_ll_isoal_mux *mux, uint8_t idx, * LLID = 0b01: Data remaining exceeds the ISO Data PDU size, * it's start or continuation fragment of an SDU. */ - *llid = rem_len > mux->max_pdu; - pdu_len = min(mux->max_pdu, rem_len); + *llid = rem_len > config->max_pdu; + pdu_len = min(config->max_pdu, rem_len); } os_mbuf_copydata(om, sdu_offset, pdu_len, dptr); @@ -384,14 +355,14 @@ ble_ll_isoal_mux_framed_get(struct ble_ll_isoal_mux *mux, uint8_t idx, hdr_len = sc ? 2 /* Segmentation Header */ : 5 /* Segmentation Header + TimeOffset */; - if (mux->max_pdu <= hdr_len + pdu_offset) { + if (mux->config.max_pdu <= hdr_len + pdu_offset) { /* Advance to next PDU */ pdu_offset = 0; num_pdu--; continue; } - frag_len = min(rem_len, mux->max_pdu - hdr_len - pdu_offset); + frag_len = min(rem_len, mux->config.max_pdu - hdr_len - pdu_offset); pdu_offset += hdr_len + frag_len; @@ -422,11 +393,11 @@ ble_ll_isoal_mux_framed_get(struct ble_ll_isoal_mux *mux, uint8_t idx, hdr_len = sc ? 2 /* Segmentation Header */ : 5 /* Segmentation Header + TimeOffset */; - if (mux->max_pdu <= hdr_len + pdu_offset) { + if (mux->config.max_pdu <= hdr_len + pdu_offset) { break; } - frag_len = min(rem_len, mux->max_pdu - hdr_len - pdu_offset); + frag_len = min(rem_len, mux->config.max_pdu - hdr_len - pdu_offset); /* Segmentation Header */ seghdr = BLE_LL_ISOAL_SEGHDR(sc, frag_len == rem_len, frag_len + hdr_len - 2); @@ -468,7 +439,7 @@ int ble_ll_isoal_mux_pdu_get(struct ble_ll_isoal_mux *mux, uint8_t idx, uint8_t *llid, void *dptr) { - if (mux->framed) { + if (mux->config.framed) { return ble_ll_isoal_mux_framed_get(mux, idx, llid, dptr); } @@ -476,13 +447,566 @@ ble_ll_isoal_mux_pdu_get(struct ble_ll_isoal_mux *mux, uint8_t idx, } void -ble_ll_isoal_init(void) +ble_ll_isoal_demux_init(struct ble_ll_isoal_demux *demux, + struct ble_ll_isoal_config *config) { + memset(demux, 0, sizeof(*demux)); + demux->config = *config; + + if (!config->framed) { + /* Core 5.3, Vol 6, Part G, 2.1 */ + demux->sdu_per_interval = config->iso_interval_us / config->sdu_interval_us; + demux->pdu_per_sdu = config->bn / demux->sdu_per_interval; + } + + STAILQ_INIT(&demux->pdu_q); } void -ble_ll_isoal_reset(void) +ble_ll_isoal_demux_reset(struct ble_ll_isoal_demux *demux) +{ + struct os_mbuf_pkthdr *pkthdr; + struct os_mbuf *om; + struct os_mbuf *om_next; + + pkthdr = STAILQ_FIRST(&demux->pdu_q); + while (pkthdr) { + om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + + while (om) { + om_next = SLIST_NEXT(om, om_next); + os_mbuf_free(om); + om = om_next; + } + + STAILQ_REMOVE_HEAD(&demux->pdu_q, omp_next); + pkthdr = STAILQ_FIRST(&demux->pdu_q); + } + + STAILQ_INIT(&demux->pdu_q); + + if (demux->frag) { + os_mbuf_free_chain(demux->frag); + demux->frag = NULL; + } +} + +int +ble_ll_isoal_mux_event_done(struct ble_ll_isoal_mux *mux) +{ + const struct ble_ll_isoal_config *config = &mux->config; + struct os_mbuf_pkthdr *pkthdr; + struct ble_mbuf_hdr *blehdr; + struct os_mbuf *om; + + pkthdr = STAILQ_FIRST(&mux->sdu_q); + if (pkthdr) { + om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + blehdr = BLE_MBUF_HDR_PTR(om); + mux->last_tx_timestamp = mux->event_tx_timestamp; + mux->last_tx_packet_seq_num = blehdr->txiso.packet_seq_num; + } + + if (config->framed) { + return ble_ll_isoal_mux_framed_event_done(mux); + } + + return ble_ll_isoal_mux_unframed_event_done(mux); +} + +int +ble_ll_isoal_demux_event_start(struct ble_ll_isoal_demux *demux, uint32_t timestamp) +{ + const struct ble_ll_isoal_config *config = &demux->config; + uint32_t elapsed_time; + uint32_t iso_offset; + uint32_t total_sdu; + uint8_t sdu_lost; + + if (config->framed) { + if (demux->active) { + elapsed_time = timestamp - demux->last_rx_timestamp; + } else { + demux->last_rx_timestamp = timestamp - config->iso_interval_us; + elapsed_time = 0; + demux->active = 1; + } + + iso_offset = elapsed_time / config->iso_interval_us; + if (iso_offset > 1) { + sdu_lost = (elapsed_time - config->iso_interval_us) / config->sdu_interval_us; + } else { + sdu_lost = 0; + } + + if (sdu_lost > 0 && demux->frag != NULL) { + /* Drop incomplete SDU */ + os_mbuf_free_chain(demux->frag); + demux->frag = NULL; + } + + total_sdu = elapsed_time / config->sdu_interval_us; + + /* Increment sequence number by number of lost SDUs */ + demux->sdu_counter += sdu_lost; + demux->last_rx_timestamp += sdu_lost * config->sdu_interval_us; + + demux->sdu_in_event = total_sdu - sdu_lost; + } else { + demux->sdu_in_event = demux->sdu_per_interval; + } + + demux->ref_time = timestamp; + + return demux->sdu_in_event; +} + +static void +ble_ll_isoal_demux_sdu_emit(struct ble_ll_isoal_demux *demux, + struct os_mbuf *om, uint32_t timestamp, bool valid) +{ + const struct ble_ll_isoal_config *config = &demux->config; + struct os_mbuf *sdu; + uint16_t sdu_len; + + sdu = om; + sdu_len = sdu != NULL ? os_mbuf_len(sdu) : 0; + + /* Core 6.0 | Vol 6, Part G, 4 + * SDUs with a length exceeding Max_SDU. In this case, the length of the + * SDU reported to the upper layer shall not exceed the Max_SDU length. The + * SDU shall be truncated to Max_SDU octets. + */ + if (sdu_len > config->max_sdu) { + BLE_LL_ASSERT(sdu != NULL); + os_mbuf_adj(sdu, -(sdu_len - config->max_sdu)); + valid = false; + } else if (sdu_len == 0 && !valid) { + /* If the SDU is empty and the SDU was reported as having errors, + * the SDU shall be discarded and reported as lost data. + */ + sdu = NULL; + } + + if (demux->cb != NULL && demux->cb->sdu_cb != NULL) { + demux->cb->sdu_cb(demux, sdu, timestamp, ++demux->sdu_counter, valid); + } + + if (om != NULL) { + os_mbuf_free_chain(om); + } + + demux->last_rx_timestamp = timestamp; + demux->sdu_in_event--; +} + +static uint8_t +pdu_idx_get(struct os_mbuf *om) { + struct ble_mbuf_hdr *hdr; + + hdr = BLE_MBUF_HDR_PTR(om); + + return POINTER_TO_UINT(hdr->rxinfo.user_data); } +static void +pdu_idx_set(struct os_mbuf *om, uint8_t pdu_idx) +{ + struct ble_mbuf_hdr *hdr; + + hdr = BLE_MBUF_HDR_PTR(om); + + hdr->rxinfo.user_data = UINT_TO_POINTER(pdu_idx); +} + +static void +sdu_ts_set(struct os_mbuf *om, uint32_t timestamp) +{ + struct ble_mbuf_hdr *hdr; + + hdr = BLE_MBUF_HDR_PTR(om); + + hdr->beg_cputime = timestamp; +} + +static uint32_t +sdu_ts_get(struct os_mbuf *om) +{ + struct ble_mbuf_hdr *hdr; + + hdr = BLE_MBUF_HDR_PTR(om); + + return hdr->beg_cputime; +} + +static uint8_t +ble_ll_isoal_demux_get_num_lost_sdu(struct ble_ll_isoal_demux *demux, uint32_t timestamp) +{ + const struct ble_ll_isoal_config *config = &demux->config; + int32_t time_diff; + + time_diff = timestamp - demux->last_rx_timestamp - config->sdu_interval_us / 2; + + return abs(time_diff) / config->sdu_interval_us; +} + +static void +ble_ll_isoal_demux_reassemble(struct ble_ll_isoal_demux *demux) +{ + const struct ble_ll_isoal_config *config = &demux->config; + struct os_mbuf_pkthdr *pdu_pkthdr; + struct os_mbuf *pdu; + struct os_mbuf *seg; + uint32_t time_offset; + uint32_t timestamp; + uint16_t seghdr; + uint8_t hdr_byte; + uint8_t llid; + uint8_t len; + uint8_t num_lost; + bool sdu_continuation; + bool cmplt; + bool valid; + + pdu = NULL; + for (uint8_t pdu_idx = 0; pdu_idx < config->bn;) { + valid = true; + + if (pdu == NULL) { + pdu_pkthdr = STAILQ_FIRST(&demux->pdu_q); + if (pdu_pkthdr == NULL) { + /* Emit incomplete SDU fragment is any */ + if (demux->frag != NULL) { + ble_ll_isoal_demux_sdu_emit(demux, demux->frag, + sdu_ts_get(demux->frag), false); + demux->frag = NULL; + } + break; + } + + pdu = OS_MBUF_PKTHDR_TO_MBUF(pdu_pkthdr); + + if (pdu_idx_get(pdu) != pdu_idx) { + /* Emit incomplete SDU fragment is any */ + if (demux->frag != NULL) { + ble_ll_isoal_demux_sdu_emit(demux, demux->frag, + sdu_ts_get(demux->frag), false); + demux->frag = NULL; + } + + /* Clear 'pdu' to pull it again in the next iteration */ + pdu_idx += pdu_idx_get(pdu) - pdu_idx; + pdu = NULL; + continue; + } + + hdr_byte = pdu->om_data[0]; + BLE_LL_ASSERT(BLE_LL_BIS_LLID_IS_DATA(hdr_byte)); + + llid = hdr_byte & BLE_LL_BIS_PDU_HDR_LLID_MASK; + if (llid != BLE_LL_BIS_LLID_DATA_PDU_FRAMED) { + /* Emit incomplete SDU fragment is any */ + if (demux->frag != NULL) { + ble_ll_isoal_demux_sdu_emit(demux, demux->frag, + sdu_ts_get(demux->frag), false); + demux->frag = NULL; + } + goto pdu_done; + } + + /* Strip the header from the buffer to process only the payload data */ + os_mbuf_adj(pdu, BLE_LL_PDU_HDR_LEN); + + if (os_mbuf_len(pdu) == 0) { + /* Padding */ + goto pdu_done; + } + } + + pdu = os_mbuf_pullup(pdu, sizeof(seghdr)); + BLE_LL_ASSERT(pdu != NULL); + + seghdr = get_le16(pdu->om_data); + os_mbuf_adj(pdu, sizeof(seghdr)); + + len = BLE_LL_ISOAL_SEGHDR_LEN(seghdr); + if (os_mbuf_len(pdu) < len) { + /* Valid if the segment length does not exceed the length of the buffer */ + len = os_mbuf_len(pdu); + valid = false; + } + + /* Duplicate and adjust buffer */ + seg = os_mbuf_dup(pdu); + + sdu_continuation = BLE_LL_ISOAL_SEGHDR_SC(seghdr); + if (!sdu_continuation) { + /* SDU Start */ + seg = os_mbuf_pullup(seg, BLE_LL_ISOAL_TIME_OFFSET_LEN); + BLE_LL_ASSERT(seg != NULL); + + time_offset = get_le24(seg->om_data); + os_mbuf_adj(seg, BLE_LL_ISOAL_TIME_OFFSET_LEN); + + timestamp = sdu_ts_get(seg) - time_offset; + + ble_ll_hci_ev_send_vs_printf(0xff, "-> timestamp: %d, sdu_ts: %d, time_offset: %d", + timestamp, sdu_ts_get(seg), time_offset); + + sdu_ts_set(seg, timestamp); + } + + /* Trim the 'pdu' head so that 'pdu->om_data' will point to the data behind SDU Segment */ + os_mbuf_adj(pdu, len); + len = os_mbuf_len(pdu); + + /* Trim the 'seg' tail so that the 'seg' will contain SDU Segment only */ + os_mbuf_adj(seg, -len); + + if (sdu_continuation) { + if (demux->frag == NULL) { + /* Drop the segment if we do not have a start segment */ + os_mbuf_free_chain(seg); + } else { + demux->frag = os_mbuf_pack_chains(demux->frag, seg); + } + } else { + /* Emit incomplete SDU fragment is any */ + if (demux->frag != NULL) { + ble_ll_isoal_demux_sdu_emit(demux, demux->frag, + sdu_ts_get(demux->frag), false); + demux->frag = NULL; + } + + /* Emit lost SDUs */ + num_lost = ble_ll_isoal_demux_get_num_lost_sdu(demux, sdu_ts_get(seg)); + for (uint8_t i = 0; i < num_lost; i++) { + timestamp = demux->last_rx_timestamp + config->sdu_interval_us; + ble_ll_isoal_demux_sdu_emit(demux, NULL, timestamp, false); + } + + demux->frag = seg; + } + + cmplt = BLE_LL_ISOAL_SEGHDR_CMPLT(seghdr); + + if (demux->frag != NULL && (cmplt || !valid)) { + ble_ll_isoal_demux_sdu_emit(demux, demux->frag, + sdu_ts_get(demux->frag), valid); + demux->frag = NULL; + } + + if (os_mbuf_len(pdu) > 0) { + continue; + } + pdu_done: + /* Free */ + STAILQ_REMOVE_HEAD(&demux->pdu_q, omp_next); + os_mbuf_free_chain(pdu); + pdu = NULL; + + pdu_idx++; + } + + timestamp = demux->ref_time + config->sdu_interval_us / 2; + num_lost = ble_ll_isoal_demux_get_num_lost_sdu(demux, timestamp); + + /* Emit lost SDUs */ + for (uint8_t i = 0; i < num_lost; i++) { + timestamp = demux->last_rx_timestamp + config->sdu_interval_us; + ble_ll_isoal_demux_sdu_emit(demux, demux->frag, timestamp, false); + demux->frag = NULL; + } +} + +static void +ble_ll_isoal_demux_recombine(struct ble_ll_isoal_demux *demux) +{ + const struct ble_ll_isoal_config *config = &demux->config; + struct os_mbuf_pkthdr *entry; + struct os_mbuf *om; + struct os_mbuf *sdu; + uint32_t timestamp; + uint8_t hdr_byte; + uint8_t llid; + bool cmplt; + bool valid; + + entry = STAILQ_FIRST(&demux->pdu_q); + for (uint8_t bn = 0; bn < config->bn; bn += demux->pdu_per_sdu) { + sdu = NULL; + valid = true; + cmplt = false; + + for (uint8_t pdu_idx = bn; pdu_idx < bn + demux->pdu_per_sdu; pdu_idx++) { + if (entry == NULL) { + /* SDU may be complete already. If so, it means we lost padding. */ + valid &= cmplt; + break; + } + + om = OS_MBUF_PKTHDR_TO_MBUF(entry); + + if (pdu_idx_get(om) != pdu_idx) { + /* SDU may be complete already. If so, it means we lost padding. */ + valid &= cmplt; + continue; + } + + /* Remove the PDU from queue */ + STAILQ_REMOVE_HEAD(&demux->pdu_q, omp_next); + + if (!valid) { + os_mbuf_free_chain(om); + entry = STAILQ_FIRST(&demux->pdu_q); + continue; + } + + hdr_byte = om->om_data[0]; + BLE_LL_ASSERT(BLE_LL_BIS_LLID_IS_DATA(hdr_byte)); + + /* Strip the header from the buffer to process only the payload data */ + os_mbuf_adj(om, BLE_LL_PDU_HDR_LEN); + + llid = hdr_byte & BLE_LL_BIS_PDU_HDR_LLID_MASK; + + if (llid == BLE_LL_BIS_LLID_DATA_PDU_UNFRAMED_CMPLT) { + /* Unframed BIS Data PDU; end fragment of an SDU or a complete SDU. */ + if (cmplt) { + /* Core 6.0 | Vol 6, Part G | 4 + * Unframed SDUs without exactly one fragment with LLID=0b00 shall be + * discarded or reported as data with errors. + */ + valid = false; + } + + if (sdu != NULL) { + os_mbuf_concat(sdu, om); + } else { + sdu = om; + } + cmplt = true; + } else if (llid == BLE_LL_BIS_LLID_DATA_PDU_UNFRAMED_SC) { + /* Unframed BIS Data PDU; start or continuation fragment of an SDU. */ + if (cmplt && om->om_len > BLE_LL_PDU_HDR_LEN) { + /* Core 6.0 | Vol 6, Part G | 4 + * Unframed SDUs where the fragment with LLID=0b00 is followed by a fragment + * with LLID=0b01 and containing at least one octet of data shall be discarded + * or reported as data with errors. + */ + valid = false; + } + + if (sdu != NULL) { + os_mbuf_concat(sdu, om); + } else { + sdu = om; + } + } else { + os_mbuf_free_chain(om); + valid = false; + } + + entry = STAILQ_FIRST(&demux->pdu_q); + } + + /* Core 5.4 | Vol 6, Part G; 3.2 + * All PDUs belonging to a burst as defined by the configuration of BN + * have the same reference anchor point. When multiple SDUs have the + * same reference anchor point, the first SDU uses the reference anchor + * point timing. Each subsequent SDU increases the SDU synchronization + * reference timing with one SDU interval. + */ + timestamp = demux->ref_time + (bn / demux->pdu_per_sdu) * config->sdu_interval_us; + + ble_ll_isoal_demux_sdu_emit(demux, sdu, timestamp, valid && cmplt); + } +} + +int +ble_ll_isoal_demux_event_done(struct ble_ll_isoal_demux *demux) +{ + const struct ble_ll_isoal_config *config = &demux->config; + struct os_mbuf_pkthdr *entry, *prev; + struct os_mbuf *om; + uint8_t idx; + + if (config->framed) { + ble_ll_isoal_demux_reassemble(demux); + } else { + ble_ll_isoal_demux_recombine(demux); + } + + prev = NULL; + entry = STAILQ_FIRST(&demux->pdu_q); + while (entry != NULL) { + om = OS_MBUF_PKTHDR_TO_MBUF(entry); + + idx = pdu_idx_get(om); + if (idx >= config->bn) { + /* Pre-transmission - update payload index only */ + pdu_idx_set(om, idx - config->bn); + + prev = entry; + entry = STAILQ_NEXT(entry, omp_next); + continue; + } + + /* Current event data */ + if (prev == NULL) { + STAILQ_REMOVE_HEAD(&demux->pdu_q, omp_next); + } else { + STAILQ_REMOVE_AFTER(&demux->pdu_q, prev, omp_next); + } + + if (prev == NULL) { + entry = STAILQ_FIRST(&demux->pdu_q); + } else { + entry = STAILQ_NEXT(prev, omp_next); + } + + os_mbuf_free(om); + } + + return 0; +} + +void +ble_ll_isoal_demux_pdu_put(struct ble_ll_isoal_demux *demux, uint8_t idx, + struct os_mbuf *pdu) +{ + struct os_mbuf_pkthdr *entry, *prev; + struct os_mbuf *om; + + sdu_ts_set(pdu, demux->ref_time); + pdu_idx_set(pdu, idx); + + prev = NULL; + entry = STAILQ_FIRST(&demux->pdu_q); + while (entry) { + om = OS_MBUF_PKTHDR_TO_MBUF(entry); + + if (pdu_idx_get(om) == idx) { + /* Already queued */ + os_mbuf_free_chain(pdu); + return; + } + + if (pdu_idx_get(om) > idx) { + /* Insert before */ + break; + } + + prev = entry; + entry = STAILQ_NEXT(entry, omp_next); + } + + entry = OS_MBUF_PKTHDR(pdu); + if (prev) { + STAILQ_INSERT_AFTER(&demux->pdu_q, prev, entry, omp_next); + } else { + STAILQ_INSERT_HEAD(&demux->pdu_q, entry, omp_next); + } +} #endif /* BLE_LL_ISO */ diff --git a/nimble/controller/src/ble_ll_scan.c b/nimble/controller/src/ble_ll_scan.c index 11f9d4faa9..7b70224074 100644 --- a/nimble/controller/src/ble_ll_scan.c +++ b/nimble/controller/src/ble_ll_scan.c @@ -1144,6 +1144,11 @@ ble_ll_scan_event_proc(struct ble_npl_event *ev) case BLE_LL_STATE_BIG: start_scan = false; break; +#endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + case BLE_LL_STATE_BIG_SYNC: + start_scan = false; + break; #endif case BLE_LL_STATE_STANDBY: break; diff --git a/nimble/controller/src/ble_ll_sched.c b/nimble/controller/src/ble_ll_sched.c index 22d55a400c..7e64385f1b 100644 --- a/nimble/controller/src/ble_ll_sched.c +++ b/nimble/controller/src/ble_ll_sched.c @@ -38,6 +38,7 @@ #endif #include "ble_ll_priv.h" #include "ble_ll_conn_priv.h" +#include "controller/ble_ll_iso_big_sync.h" #define BLE_LL_SCHED_MAX_DELAY_ANY (0x7fffffff) @@ -192,6 +193,12 @@ ble_ll_sched_preempt(struct ble_ll_sched_item *sch, case BLE_LL_SCHED_TYPE_EXTERNAL: ble_ll_ext_sched_removed(entry); break; +#endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + case BLE_LL_SCHED_TYPE_BIG_SYNC: + /* FIXME sometimes it may be useful to preempt... */ + BLE_LL_ASSERT(0); + break; #endif default: BLE_LL_ASSERT(0); @@ -875,6 +882,25 @@ ble_ll_sched_iso_big(struct ble_ll_sched_item *sch, int first, int fixed) } #endif /* BLE_LL_ISO_BROADCASTER */ +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) +int +ble_ll_sched_iso_big_sync(struct ble_ll_sched_item *sch) +{ + os_sr_t sr; + int rc; + + OS_ENTER_CRITICAL(sr); + + rc = ble_ll_sched_insert(sch, 0, preempt_any); + + OS_EXIT_CRITICAL(sr); + + ble_ll_sched_restart(); + + return rc; +} +#endif + /** * Remove a schedule element * @@ -1023,6 +1049,11 @@ ble_ll_sched_execute_item(struct ble_ll_sched_item *sch) ble_ll_iso_big_halt(); break; #endif +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + case BLE_LL_STATE_BIG_SYNC: + ble_ll_iso_big_sync_halt(); + break; +#endif #if MYNEWT_VAL(BLE_LL_EXT) case BLE_LL_STATE_EXTERNAL: ble_ll_ext_halt(); @@ -1118,6 +1149,17 @@ ble_ll_sched_next_time(uint32_t *next_event_time) } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static int +preempt_any_except_big_sync(struct ble_ll_sched_item *sch, + struct ble_ll_sched_item *item) +{ + if (item->sched_type != BLE_LL_SCHED_TYPE_BIG_SYNC) { + return 1; + } + + return 0; +} + int ble_ll_sched_scan_aux(struct ble_ll_sched_item *sch) { @@ -1126,7 +1168,7 @@ ble_ll_sched_scan_aux(struct ble_ll_sched_item *sch) OS_ENTER_CRITICAL(sr); - rc = ble_ll_sched_insert(sch, 0, preempt_none); + rc = ble_ll_sched_insert(sch, 0, preempt_any_except_big_sync); OS_EXIT_CRITICAL(sr); diff --git a/nimble/controller/src/ble_ll_sync.c b/nimble/controller/src/ble_ll_sync.c index f1a709fb9e..1b34b93537 100644 --- a/nimble/controller/src/ble_ll_sync.c +++ b/nimble/controller/src/ble_ll_sync.c @@ -126,6 +126,11 @@ struct ble_ll_sync_sm { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_ADI_SUPPORT) uint16_t prev_adi; #endif + +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + ble_ll_sync_biginfo_cb_t biginfo_cb; + void *biginfo_cb_arg; +#endif }; static struct ble_ll_sync_sm g_ble_ll_sync_sm[BLE_LL_SYNC_CNT]; @@ -673,11 +678,37 @@ ble_ll_sync_send_truncated_per_adv_rpt(struct ble_ll_sync_sm *sm, uint8_t *evbuf } #if MYNEWT_VAL(BLE_LL_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) +static uint8_t +ble_ll_sync_biginfo_phy_get(const uint8_t *biginfo) +{ + uint8_t phy; + + phy = (biginfo[27] >> 5) & 0x07; + switch (phy) { + case 0: + return BLE_PHY_1M; + case 1: + return BLE_PHY_2M; + case 2: + return BLE_PHY_CODED; + default: + return 0x00; + } +} + +static uint8_t +ble_ll_sync_biginfo_bn_get(const uint8_t *biginfo) +{ + return (biginfo[4] >> 5) & 0x07; +} + static void ble_ll_sync_parse_biginfo_to_ev(struct ble_hci_ev_le_subev_biginfo_adv_report *ev, const uint8_t *biginfo, uint8_t biginfo_len) { uint32_t fields_buf; + uint8_t framing_mode; + uint8_t framed; fields_buf = get_le32(&biginfo[0]); ev->iso_interval = (fields_buf >> 15) & 0x0FFF; @@ -685,7 +716,7 @@ ble_ll_sync_parse_biginfo_to_ev(struct ble_hci_ev_le_subev_biginfo_adv_report *e fields_buf = get_le32(&biginfo[4]); ev->nse = fields_buf & 0x1F; - ev->bn = (fields_buf >> 5) & 0x07; + ev->bn = ble_ll_sync_biginfo_bn_get(biginfo); ev->pto = (fields_buf >> 28) & 0x0F; fields_buf = get_le32(&biginfo[8]); @@ -693,14 +724,20 @@ ble_ll_sync_parse_biginfo_to_ev(struct ble_hci_ev_le_subev_biginfo_adv_report *e ev->max_pdu = (fields_buf >> 24) & 0xFF; fields_buf = get_le32(&biginfo[17]); - ev->sdu_interval[0] = fields_buf & 0xFF; - ev->sdu_interval[1] = (fields_buf >> 8) & 0xFF; - ev->sdu_interval[2] = (fields_buf >> 16) & 0x0F; + put_le24(ev->sdu_interval, fields_buf & 0x0FFFFF); ev->max_sdu = (fields_buf >> 20) & 0x0FFF; - ev->phy = (biginfo[27] >> 5) & 0x07; + ev->phy = ble_ll_sync_biginfo_phy_get(biginfo); - ev->framing = (biginfo[32] >> 7) & 0x01; + framed = (biginfo[32] >> 7) & 0x01; + framing_mode = (biginfo[12] >> 7) & 0x01; + if (framed && framing_mode) { + ev->framing = BLE_HCI_ISO_FRAMING_FRAMED_UNSEGMENTED; + } else if (framed) { + ev->framing = BLE_HCI_ISO_FRAMING_FRAMED_SEGMENTABLE; + } else { + ev->framing = BLE_HCI_ISO_FRAMING_UNFRAMED; + } if (biginfo_len == BLE_LL_SYNC_BIGINFO_LEN_ENC) { ev->encryption = 1; @@ -715,10 +752,6 @@ ble_ll_sync_send_biginfo_adv_rpt(struct ble_ll_sync_sm *sm, const uint8_t *bigin struct ble_hci_ev_le_subev_biginfo_adv_report *ev; struct ble_hci_ev *hci_ev; - if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_BIGINFO_ADV_REPORT)) { - return; - } - hci_ev = ble_transport_alloc_evt(1); if (!hci_ev) { return; @@ -1135,6 +1168,66 @@ ble_ll_sync_check_acad(struct ble_ll_sync_sm *sm, return true; } +#if MYNEWT_VAL(BLE_LL_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) +void +ble_ll_sync_biginfo_cb_set(struct ble_ll_sync_sm *syncsm, + ble_ll_sync_biginfo_cb_t cb, void *cb_arg) +{ + syncsm->biginfo_cb = cb; + syncsm->biginfo_cb_arg = cb_arg; +} +#endif + +static void +ble_ll_sync_biginfo_handle(struct ble_ll_sync_sm *sm, struct ble_mbuf_hdr *hdr, + const uint8_t *biginfo, uint8_t biginfo_len, + bool is_duplicate) +{ + bool reports_enabled; + uint8_t phy; + + /* The BIGInfo length is 33 octets for an unencrypted BIG and 57 octets for an encrypted BIG. */ + if (biginfo_len != BLE_LL_SYNC_BIGINFO_LEN && + biginfo_len != BLE_LL_SYNC_BIGINFO_LEN_ENC) { + return; + } + + phy = ble_ll_sync_biginfo_phy_get(biginfo); + if (!ble_ll_phy_is_supported(phy)) { + /* Core 6.0 | Vol 6, Part B, 4.4.5.2 + * If the PHY field of the BIGInfo specifies a PHY that the Link Layer does not support or is + * reserved for future use, the Link Layer shall ignore the BIGInfo, shall not report the + * BIGInfo to the Host, and shall not enter the Synchronization state for the BIG specified + * in the BIGinfo. + */ + return; + } + + reports_enabled = + ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_BIGINFO_ADV_REPORT) && + !is_duplicate; + +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + uint8_t bn; + + bn = ble_ll_sync_biginfo_bn_get(biginfo); + reports_enabled &= bn <= MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC_MAX_BN); +#endif + + if (reports_enabled) { + ble_ll_sync_send_biginfo_adv_rpt(sm, biginfo, biginfo_len); + } + +#if MYNEWT_VAL(BLE_LL_ISO_BROADCAST_SYNC) + if (sm->biginfo_cb) { + sm->biginfo_cb(sm, sm->sca, hdr->beg_cputime, hdr->rem_usecs, biginfo, + biginfo_len, sm->biginfo_cb_arg); + } +#endif +} +#endif + void ble_ll_sync_rx_pkt_in(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr) { @@ -1239,13 +1332,13 @@ ble_ll_sync_rx_pkt_in(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr) /* send reports from this PDU */ ble_ll_sync_send_per_adv_rpt(sm, rxpdu, hdr->rxinfo.rssi, tx_power, datalen, aux, aux_scheduled); + } #if MYNEWT_VAL(BLE_LL_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) - if (biginfo) { - ble_ll_sync_send_biginfo_adv_rpt(sm, biginfo, biginfo_len); - } -#endif + if (biginfo && !(sm->flags & BLE_LL_SYNC_SM_FLAG_DISABLED)) { + ble_ll_sync_biginfo_handle(sm, hdr, biginfo, biginfo_len, is_duplicate); } +#endif /* if chain was scheduled we don't end event yet */ /* TODO should we check resume only if offset is high? */ @@ -2384,6 +2477,24 @@ ble_ll_sync_rmvd_from_sched(struct ble_ll_sync_sm *sm) ble_ll_event_add(&sm->sync_ev_end); } +struct ble_ll_sync_sm * +ble_ll_sync_get(uint8_t handle) +{ + struct ble_ll_sync_sm *syncsm; + + if (handle >= BLE_LL_SYNC_CNT) { + return NULL; + } + + syncsm = &g_ble_ll_sync_sm[handle]; + + if (!syncsm->flags) { + return NULL; + } + + return syncsm; +} + bool ble_ll_sync_enabled(void) { diff --git a/nimble/controller/syscfg.yml b/nimble/controller/syscfg.yml index a7b75d83fb..0eb0c60214 100644 --- a/nimble/controller/syscfg.yml +++ b/nimble/controller/syscfg.yml @@ -527,7 +527,28 @@ syscfg.defs: - BLE_LL_ISO if 1 value: 0 state: experimental - + BLE_LL_ISO_BROADCAST_SYNC: + description: > + Enable support for Isochronous Broadcast Streams Synchronization state. + restrictions: + - BLE_LL_ISO if 1 + value: 0 + state: experimental + BLE_LL_ISO_BROADCAST_SYNC_MAX_BIG: + description: > + Maximum number of concurrent broadcast isochronous groups sync. + value: 1 + experimental: 1 + BLE_LL_ISO_BROADCAST_SYNC_MAX_BIS: + description: > + Maximum number of concurrent broadcast isochronous streams per group. + value: 2 + experimental: 1 + BLE_LL_ISO_BROADCAST_SYNC_MAX_BN: + description: > + Maximum number of bursts that can be received per broadcast isochronous stream event. + value: 2 + experimental: 1 BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS: description: > Enables ISO synchronization feedback using vendor-specific HCI event. @@ -685,6 +706,9 @@ syscfg.vals.BLE_LL_ISO_BROADCASTER: BLE_LL_CFG_FEAT_LE_ENCRYPTION: 1 BLE_LL_STACK_SIZE: 200 +syscfg.vals.BLE_LL_ISO_BROADCAST_SYNC: + BLE_LL_PERIODIC_ADV_SYNC_BIGINFO_REPORTS: 1 + # Enable vendor event on assert in standalone build to make failed assertions in # controller code visible when connected to external host syscfg.vals.'!BLE_HOST && !BABBLESIM': diff --git a/nimble/controller/test/src/ble_ll_iso.c b/nimble/controller/test/src/ble_ll_iso.c index 2f88214c93..e04cc1b12c 100644 --- a/nimble/controller/test/src/ble_ll_iso.c +++ b/nimble/controller/test/src/ble_ll_iso.c @@ -17,6 +17,7 @@ * under the License. */ +#include "../../src/ble_ll_iso_priv.h" #include #include #include @@ -80,6 +81,52 @@ const struct test_ll_common_params test_ll_common_params_bn_1 = { .Sync_Timeout = 100, }; +struct test_ll_iso_fixture { + struct ble_ll_iso_params params; + struct ble_ll_iso_conn conn; + struct ble_ll_iso_rx rx; + struct ble_ll_iso_tx tx; +}; + +static void +test_ll_iso_setup(struct test_ll_iso_fixture *fixture, + const struct test_ll_common_params *params) +{ + struct ble_ll_iso_params *iso_params; + struct ble_ll_iso_conn *conn; + struct ble_ll_iso_rx *rx; + struct ble_ll_iso_tx *tx; + + memset(fixture, 0, sizeof(*fixture)); + iso_params = &fixture->params; + conn = &fixture->conn; + rx = &fixture->rx; + tx = &fixture->tx; + + iso_params->iso_interval = params->ISO_Interval * 1000 / 1250; + iso_params->sdu_interval = params->SDU_Interval * 1000; + iso_params->max_sdu = TSPX_max_tx_payload; + iso_params->max_pdu = TSPX_max_tx_payload; + iso_params->bn = params->BN; + iso_params->pte = 0; + iso_params->framed = params->Framing != 0x00; + iso_params->framing_mode = params->Framing == 0x02; + + ble_ll_iso_conn_init(conn, 0x0001, rx, tx); + ble_ll_iso_rx_init(rx, conn, iso_params); + ble_ll_iso_tx_init(tx, conn, iso_params); +} + +static void +test_ll_iso_teardown(struct test_ll_iso_fixture *fixture) +{ + struct ble_ll_iso_conn *conn; + + conn = &fixture->conn; + + ble_ll_iso_conn_reset(conn); +} + TEST_CASE_SELF(test_ll_ist_brd_bv_01_c) { const uint8_t payload_types[] = { BLE_HCI_PAYLOAD_TYPE_ZERO_LENGTH, @@ -93,34 +140,30 @@ TEST_CASE_SELF(test_ll_ist_brd_bv_01_c) { struct ble_hci_le_iso_transmit_test_rp iso_transmit_test_rp; struct ble_hci_le_iso_test_end_cp iso_test_end_cp; struct ble_hci_le_iso_test_end_rp iso_test_end_rp; - struct ble_ll_iso_conn_init_param conn_param = { - .iso_interval_us = params->SDU_Interval * 1000, - .sdu_interval_us = params->SDU_Interval * 1000, - .conn_handle = 0x0001, - .max_sdu = TSPX_max_tx_payload, - .max_pdu = TSPX_max_tx_payload, - .framing = params->Framing, - .bn = params->BN - }; - struct ble_ll_iso_conn conn; + struct test_ll_iso_fixture fixture; + struct ble_ll_iso_conn *conn; uint8_t payload_type; uint8_t pdu[100]; uint8_t llid; uint8_t rsplen = 0; int rc; - ble_ll_iso_conn_init(&conn, &conn_param); + test_ll_iso_setup(&fixture, params); + + conn = &fixture.conn; for (uint8_t i = 0; i < ARRAY_SIZE(payload_types); i++) { payload_type = payload_types[i]; - /* 2. The Upper Tester sends the HCI_LE_ISO_Transmit_Test command with Payload_Type as - * specified in Table 4.12-2 and receives a successful HCI_Command_Complete event from the IUT in response. + /* 2. The Upper Tester sends the HCI_LE_ISO_Transmit_Test command with + * Payload_Type as specified in Table 4.12-2 and receives a successful + * HCI_Command_Complete event from the IUT in response. */ rsplen = 0xFF; - iso_transmit_test_cp.conn_handle = htole16(conn.handle); + iso_transmit_test_cp.conn_handle = htole16(conn->handle); iso_transmit_test_cp.payload_type = payload_type; - rc = ble_ll_iso_transmit_test((uint8_t *)&iso_transmit_test_cp, sizeof(iso_transmit_test_cp), + rc = ble_ll_iso_transmit_test((uint8_t *)&iso_transmit_test_cp, + sizeof(iso_transmit_test_cp), (uint8_t *)&iso_transmit_test_rp, &rsplen); TEST_ASSERT(rc == 0); TEST_ASSERT(rsplen == sizeof(iso_transmit_test_rp)); @@ -131,12 +174,12 @@ TEST_CASE_SELF(test_ll_ist_brd_bv_01_c) { * 4. Repeat step 3 for a total of 5 payloads. */ for (uint8_t j = 0; j < 5; j++) { - rc = ble_ll_iso_conn_event_start(&conn, 30000); + rc = ble_ll_iso_tx_event_start(conn->tx, 30000); TEST_ASSERT(rc == 0); - for (uint8_t k = 0; k < conn_param.bn; k++) { + for (uint8_t k = 0; k < conn->tx->params->bn; k++) { llid = 0xFF; - rc = ble_ll_iso_pdu_get(&conn, k, k, &llid, pdu); + rc = ble_ll_iso_tx_pdu_get(conn->tx, k, k, &llid, pdu); if (payload_type == BLE_HCI_PAYLOAD_TYPE_ZERO_LENGTH) { TEST_ASSERT(rc == 0); TEST_ASSERT(llid == 0b00); @@ -144,23 +187,26 @@ TEST_CASE_SELF(test_ll_ist_brd_bv_01_c) { TEST_ASSERT(rc >= 4); TEST_ASSERT(llid == 0b00); } else if (payload_type == BLE_HCI_PAYLOAD_TYPE_MAXIMUM_LENGTH) { - TEST_ASSERT(rc == conn_param.max_pdu); + TEST_ASSERT(rc == conn->tx->params->max_pdu); TEST_ASSERT(llid == 0b00); } } - rc = ble_ll_iso_conn_event_done(&conn); + rc = ble_ll_iso_tx_event_done(conn->tx); TEST_ASSERT(rc == 0); } /* 5. The Upper Tester sends an HCI_LE_Setup_ISO_Data_Path command to the IUT. - * 6. The IUT sends an HCI_Command_Complete event to the Upper Tester with Status set to 0x0C. + * 6. The IUT sends an HCI_Command_Complete event to the Upper Tester with Status set to + * 0x0C. */ - setup_iso_data_path_cp.conn_handle = htole16(conn.handle); + memset(&setup_iso_data_path_cp, 0, sizeof(setup_iso_data_path_cp)); + setup_iso_data_path_cp.conn_handle = htole16(conn->handle); setup_iso_data_path_cp.data_path_dir = 0x00; setup_iso_data_path_cp.data_path_id = 0x00; - rc = ble_ll_iso_setup_iso_data_path((uint8_t *)&setup_iso_data_path_cp, sizeof(setup_iso_data_path_cp), - (uint8_t *)&setup_iso_data_path_rp, &rsplen); + rc = ble_ll_iso_setup_iso_data_path( + (uint8_t *)&setup_iso_data_path_cp, sizeof(setup_iso_data_path_cp), + (uint8_t *)&setup_iso_data_path_rp, &rsplen); TEST_ASSERT(rc == 0x0C); /* 7. The Upper Tester sends the HCI_LE_ISO_Test_End command to the IUT and receives an @@ -168,7 +214,7 @@ TEST_CASE_SELF(test_ll_ist_brd_bv_01_c) { * Received_SDU_Count, Missed_SDU_Count, and Failed_SDU_Count are all zero. */ rsplen = 0xFF; - iso_test_end_cp.conn_handle = htole16(conn.handle); + iso_test_end_cp.conn_handle = htole16(conn->handle); rc = ble_ll_iso_end_test((uint8_t *)&iso_test_end_cp, sizeof(iso_test_end_cp), (uint8_t *)&iso_test_end_rp, &rsplen); TEST_ASSERT(rc == 0); @@ -179,13 +225,114 @@ TEST_CASE_SELF(test_ll_ist_brd_bv_01_c) { TEST_ASSERT(iso_test_end_rp.failed_sdu_count == 0); } - ble_ll_iso_conn_free(&conn); + test_ll_iso_teardown(&fixture); +} + +struct test_ll_iso_receive_test_mode_round { + uint8_t framing; + uint8_t payload_type; +}; + +TEST_CASE_SELF(test_ll_ist_snc_bv_01_c) +{ + const struct test_ll_iso_receive_test_mode_round rounds[] = { + { 0, 0 }, + { 0, 1 }, + { 0, 2 }, + { 1, 2 }, + { 1, 1 } + }; + const struct test_ll_common_params *params = &test_ll_common_params_bn_1; + struct ble_hci_le_iso_read_test_counters_cp iso_read_test_counters_cp; + struct ble_hci_le_iso_read_test_counters_rp iso_read_test_counters_rp; + struct ble_hci_le_setup_iso_data_path_cp setup_iso_data_path_cp; + struct ble_hci_le_setup_iso_data_path_rp setup_iso_data_path_rp; + struct ble_hci_le_iso_receive_test_cp iso_receive_test_cp; + struct ble_hci_le_iso_receive_test_rp iso_receive_test_rp; + struct ble_hci_le_iso_test_end_cp iso_test_end_cp; + struct ble_hci_le_iso_test_end_rp iso_test_end_rp; + struct test_ll_iso_fixture fixture; + struct ble_ll_iso_conn *conn; + uint8_t payload_type; + uint8_t rsplen = 0; + int rc; + + test_ll_iso_setup(&fixture, params); + + conn = &fixture.conn; + + for (uint8_t i = 0; i < ARRAY_SIZE(rounds); i++) { + payload_type = rounds[i].payload_type; + + /* 3. The Upper Tester sends the HCI_LE_ISO_Receive_Test command to the + * IUT with Payload_Type as specified in Table 4.12-4 and receives a + * successful HCI_Command_Complete event from the IUT in response. + */ + rsplen = 0xFF; + iso_receive_test_cp.conn_handle = htole16(conn->handle); + iso_receive_test_cp.payload_type = payload_type; + rc = ble_ll_iso_receive_test((uint8_t *)&iso_receive_test_cp, + sizeof(iso_receive_test_cp), + (uint8_t *)&iso_receive_test_rp, &rsplen); + TEST_ASSERT(rc == 0); + TEST_ASSERT(rsplen == sizeof(iso_receive_test_rp)); + TEST_ASSERT(iso_receive_test_rp.conn_handle == iso_receive_test_cp.conn_handle); + + /* 6. Repeat steps 4 and 5 for a total of 5 payloads. */ + for (uint8_t j = 0; j < 5; j++) { + /* 4. TODO The Lower Tester sends an SDU as specified in Table 4.12-4. */ + /* 5. TODO The IUT receives the expected test Payload PDU as specified in Table. */ + } + + /* 7. The Upper Tester sends the HCI_LE_ISO_Read_Test_Counters command to the IUT. */ + rsplen = 0xFF; + iso_read_test_counters_cp.conn_handle = htole16(conn->handle); + rc = ble_ll_iso_read_counters_test((uint8_t *)&iso_read_test_counters_cp, + sizeof(iso_read_test_counters_cp), + (uint8_t *)&iso_read_test_counters_rp, + &rsplen); + TEST_ASSERT(rc == 0); + TEST_ASSERT(rsplen == sizeof(iso_read_test_counters_rp)); + TEST_ASSERT(iso_read_test_counters_rp.conn_handle == + iso_read_test_counters_cp.conn_handle); + + /* 8. TODO The Upper Tester receives a return parameter of + * Received_SDU_Count as specified in Table 4.12-4. + */ + + /* 9. The Upper Tester sends an HCI_LE_Setup_ISO_Data_Path command to the IUT. + * 10. The IUT sends an HCI_Command_Complete event to the Upper Tester with + * Status set to 0x0C. + */ + memset(&setup_iso_data_path_cp, 0, sizeof(setup_iso_data_path_cp)); + setup_iso_data_path_cp.conn_handle = htole16(conn->handle); + setup_iso_data_path_cp.data_path_dir = 0x01; + setup_iso_data_path_cp.data_path_id = 0x00; + rc = ble_ll_iso_setup_iso_data_path( + (uint8_t *)&setup_iso_data_path_cp, sizeof(setup_iso_data_path_cp), + (uint8_t *)&setup_iso_data_path_rp, &rsplen); + TEST_ASSERT(rc == 0x0C); + + /* 11. The Upper Tester sends the HCI_LE_ISO_Test_End command to the IUT and receives an + * HCI_Command_Status event from the IUT with the Status field set to Success. + */ + rsplen = 0xFF; + iso_test_end_cp.conn_handle = htole16(conn->handle); + rc = ble_ll_iso_end_test((uint8_t *)&iso_test_end_cp, sizeof(iso_test_end_cp), + (uint8_t *)&iso_test_end_rp, &rsplen); + TEST_ASSERT(rc == 0); + TEST_ASSERT(rsplen == sizeof(iso_test_end_rp)); + TEST_ASSERT(iso_test_end_rp.conn_handle == iso_test_end_cp.conn_handle); + } + + test_ll_iso_teardown(&fixture); } TEST_SUITE(ble_ll_iso_test_suite) { ble_ll_iso_init(); test_ll_ist_brd_bv_01_c(); + test_ll_ist_snc_bv_01_c(); ble_ll_iso_reset(); } diff --git a/nimble/controller/test/src/ble_ll_isoal.c b/nimble/controller/test/src/ble_ll_isoal.c index b7c0aa2c35..909399f0f3 100644 --- a/nimble/controller/test/src/ble_ll_isoal.c +++ b/nimble/controller/test/src/ble_ll_isoal.c @@ -17,68 +17,180 @@ * under the License. */ -#include +#include "../../src/ble_ll_iso_priv.h" #include -#include +#include #include #include +#include +#include +#include #include -#define TSPX_max_sdu_length (503) -#define HCI_iso_sdu_max (MYNEWT_VAL(BLE_TRANSPORT_ISO_SIZE) - 4) +struct test_ial_userhdr { + uint32_t timestamp; + uint16_t seq_num; + uint8_t pkt_status; +}; + +#define TSPX_max_sdu_length (754) +#define HCI_iso_sdu_max (MYNEWT_VAL(BLE_TRANSPORT_ISO_SIZE) - 4) + +#define TEST_PKTHDR_OVERHEAD \ + (sizeof(struct os_mbuf_pkthdr) + sizeof(struct test_ial_userhdr)) +#define TEST_BUF_OVERHEAD (sizeof(struct os_mbuf) + TEST_PKTHDR_OVERHEAD) -#define MBUF_TEST_POOL_BUF_SIZE (TSPX_max_sdu_length + BLE_MBUF_MEMBLOCK_OVERHEAD) -#define MBUF_TEST_POOL_BUF_COUNT (10) +#define TEST_DATA_PKT_LEN \ + (TSPX_max_sdu_length + sizeof(struct ble_hci_iso_data)) +#define TEST_DATA_BUF_SIZE \ + OS_ALIGN(TEST_DATA_PKT_LEN + sizeof(struct ble_hci_iso), 4) -os_membuf_t os_mbuf_membuf[OS_MEMPOOL_SIZE(MBUF_TEST_POOL_BUF_SIZE, MBUF_TEST_POOL_BUF_COUNT)]; +#define TEST_BUF_COUNT (20) +#define TEST_BUF_SIZE (TEST_DATA_BUF_SIZE + TEST_BUF_OVERHEAD) +#define TEST_BUF_POOL_SIZE OS_MEMPOOL_SIZE(TEST_BUF_COUNT, TEST_BUF_SIZE) -static struct os_mbuf_pool os_mbuf_pool; -static struct os_mempool os_mbuf_mempool; -static uint8_t os_mbuf_test_data[TSPX_max_sdu_length]; +static struct os_mbuf_pool g_mbuf_pool; +static struct os_mempool g_mbuf_mempool; +static os_membuf_t g_mbuf_buffer[TEST_BUF_POOL_SIZE]; +static uint8_t g_test_sdu_data[TSPX_max_sdu_length]; + +STAILQ_HEAD(, os_mbuf_pkthdr) sdu_q = STAILQ_HEAD_INITIALIZER(sdu_q); void -os_mbuf_test_setup(void) +ble_ll_isoal_test_suite_init(void) { int rc; int i; - rc = os_mempool_init(&os_mbuf_mempool, MBUF_TEST_POOL_BUF_COUNT, - MBUF_TEST_POOL_BUF_SIZE, &os_mbuf_membuf[0], "mbuf_pool"); + rc = os_mempool_init(&g_mbuf_mempool, TEST_BUF_COUNT, TEST_BUF_SIZE, + &g_mbuf_buffer[0], "mbuf_pool"); TEST_ASSERT_FATAL(rc == 0, "Error creating memory pool %d", rc); - rc = os_mbuf_pool_init(&os_mbuf_pool, &os_mbuf_mempool, - MBUF_TEST_POOL_BUF_SIZE, MBUF_TEST_POOL_BUF_COUNT); + rc = os_mbuf_pool_init(&g_mbuf_pool, &g_mbuf_mempool, TEST_BUF_SIZE, TEST_BUF_COUNT); TEST_ASSERT_FATAL(rc == 0, "Error creating mbuf pool %d", rc); - for (i = 0; i < sizeof os_mbuf_test_data; i++) { - os_mbuf_test_data[i] = i; + for (i = 0; i < sizeof g_test_sdu_data; i++) { + g_test_sdu_data[i] = i; + } +} + +static void +isoal_demux_sdu_cb(struct ble_ll_isoal_demux *demux, const struct os_mbuf *sdu, + uint32_t timestamp, uint16_t seq_num, bool valid) +{ + struct test_ial_userhdr *userhdr; + struct os_mbuf_pkthdr *pkthdr; + struct os_mbuf *om; + uint16_t sdu_len; + void *data; + + om = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(*userhdr)); + TEST_ASSERT_FATAL(om != NULL); + + userhdr = OS_MBUF_USRHDR(om); + if (sdu == NULL) { + userhdr->pkt_status = BLE_HCI_ISO_PKT_STATUS_LOST; + } else if (valid) { + userhdr->pkt_status = BLE_HCI_ISO_PKT_STATUS_VALID; + } else { + userhdr->pkt_status = BLE_HCI_ISO_PKT_STATUS_INVALID; + } + userhdr->timestamp = timestamp; + userhdr->seq_num = seq_num; + + if (sdu != NULL) { + sdu_len = os_mbuf_len(sdu); + + data = os_mbuf_extend(om, sdu_len); + TEST_ASSERT_FATAL(data != NULL); + + os_mbuf_copydata(sdu, 0, sdu_len, data); } - TEST_ASSERT_FATAL(os_mbuf_mempool.mp_block_size == MBUF_TEST_POOL_BUF_SIZE, - "mp_block_size is %d", os_mbuf_mempool.mp_block_size); - TEST_ASSERT_FATAL(os_mbuf_mempool.mp_num_free == MBUF_TEST_POOL_BUF_COUNT, - "mp_num_free is %d", os_mbuf_mempool.mp_num_free); + pkthdr = OS_MBUF_PKTHDR(om); + STAILQ_INSERT_TAIL(&sdu_q, pkthdr, omp_next); } -TEST_CASE_SELF(test_ble_ll_isoal_mux_init) { +static struct ble_ll_isoal_demux_cb isoal_demux_cb = { + .sdu_cb = isoal_demux_sdu_cb, +}; + +struct test_ll_isoal_fixture { struct ble_ll_isoal_mux mux; + struct ble_ll_isoal_demux demux; +}; + +static void +test_ll_isoal_setup(struct test_ll_isoal_fixture *fixture, uint16_t max_sdu, + uint8_t max_pdu, uint32_t iso_interval_us, + uint32_t sdu_interval_us, uint8_t bn, bool framed, bool unsegmented) +{ + struct ble_ll_isoal_config config = { + .iso_interval_us = iso_interval_us, + .sdu_interval_us = sdu_interval_us, + .max_sdu = max_sdu, + .max_pdu = max_pdu, + .bn = bn, + .pte = 0, + .framed = framed, + .unsegmented = unsegmented, + }; + + ble_ll_isoal_mux_init(&fixture->mux, &config); + + ble_ll_isoal_demux_init(&fixture->demux, &config); + ble_ll_isoal_demux_cb_set(&fixture->demux, &isoal_demux_cb); + + STAILQ_INIT(&sdu_q); +} + +static void +test_ll_isoal_teardown(struct test_ll_isoal_fixture *fixture) +{ + struct os_mbuf_pkthdr *pkthdr; + struct os_mbuf *om; + + ble_ll_isoal_mux_reset(&fixture->mux); + ble_ll_isoal_demux_reset(&fixture->demux); + + pkthdr = STAILQ_FIRST(&sdu_q); + while (pkthdr) { + om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + + os_mbuf_free_chain(om); + + STAILQ_REMOVE_HEAD(&sdu_q, omp_next); + + pkthdr = STAILQ_FIRST(&sdu_q); + } + + STAILQ_INIT(&sdu_q); + + /* Memory leak test */ + TEST_ASSERT_FATAL(g_mbuf_mempool.mp_block_size == TEST_BUF_SIZE, + "mp_block_size is %d", g_mbuf_mempool.mp_block_size); + TEST_ASSERT_FATAL(g_mbuf_mempool.mp_num_free == TEST_BUF_COUNT, + "mp_num_free is %d", g_mbuf_mempool.mp_num_free); +} + +TEST_CASE_SELF(test_ble_ll_isoal_mux_init) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; const uint32_t iso_interval_us = 10000; const uint32_t sdu_interval_us = 10000; - const bool Framed = 0; - const bool Framing_Mode = 0; - const uint8_t bn = 1; - const uint8_t max_pdu = 250; - ble_ll_isoal_mux_init(&mux, max_pdu, iso_interval_us, sdu_interval_us, bn, - 0, Framed, Framing_Mode); + test_ll_isoal_setup(&fixture, 250, 250, iso_interval_us, sdu_interval_us, + 1, false, false); - TEST_ASSERT(mux.pdu_per_sdu == (bn * sdu_interval_us) / iso_interval_us); + TEST_ASSERT(mux->pdu_per_sdu == sdu_interval_us / iso_interval_us); - ble_ll_isoal_mux_free(&mux); + test_ll_isoal_teardown(&fixture); } TEST_CASE_SELF(ble_ll_isoal_mux_pdu_get_unframed_1_sdu_2_pdu) { - struct ble_ll_isoal_mux mux; + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; struct os_mbuf *sdu_1, *sdu_2; const uint32_t iso_interval_us = 20000; const uint32_t sdu_interval_us = 10000; @@ -93,69 +205,70 @@ TEST_CASE_SELF(ble_ll_isoal_mux_pdu_get_unframed_1_sdu_2_pdu) { uint8_t llid = 0x00; int rc; - ble_ll_isoal_mux_init(&mux, max_pdu, iso_interval_us, sdu_interval_us, bn, - 0, Framed, Framing_Mode); + test_ll_isoal_setup(&fixture, sdu_len, max_pdu, iso_interval_us, + sdu_interval_us, bn, Framed, Framing_Mode); /* SDU #1 */ - sdu_1 = os_mbuf_get_pkthdr(&os_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + sdu_1 = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); TEST_ASSERT_FATAL(sdu_1 != NULL); - rc = os_mbuf_append(sdu_1, os_mbuf_test_data, sdu_len); + rc = os_mbuf_append(sdu_1, g_test_sdu_data, sdu_len); TEST_ASSERT_FATAL(rc == 0); - ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_1); + ble_ll_isoal_mux_sdu_put(mux, sdu_1); /* SDU #2 */ - sdu_2 = os_mbuf_get_pkthdr(&os_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + sdu_2 = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); TEST_ASSERT_FATAL(sdu_2 != NULL); - rc = os_mbuf_append(sdu_2, os_mbuf_test_data, sdu_len); + rc = os_mbuf_append(sdu_2, g_test_sdu_data, sdu_len); TEST_ASSERT_FATAL(rc == 0); - ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_2); + ble_ll_isoal_mux_sdu_put(mux, sdu_2); - ble_ll_isoal_mux_event_start(&mux, 90990); + ble_ll_isoal_mux_event_start(mux, 90990); /* PDU #1 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; start or continuation fragment of an SDU. */ TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); /* PDU #2 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; start or continuation fragment of an SDU. */ TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); /* PDU #3 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 2, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 2, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; end fragment of an SDU or a complete SDU. */ TEST_ASSERT(llid == 0b00, "LLID is incorrect %d", llid); /* PDU #4 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; start or continuation fragment of an SDU. */ TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); /* PDU #5 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; start or continuation fragment of an SDU. */ TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); /* PDU #6 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 2, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 2, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; end fragment of an SDU or a complete SDU. */ TEST_ASSERT(llid == 0b00, "LLID is incorrect %d", llid); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt > 0, "num_completed_pkt is incorrect %d", num_completed_pkt); - ble_ll_isoal_mux_free(&mux); + test_ll_isoal_teardown(&fixture); } TEST_CASE_SELF(test_ble_ll_isoal_mux_get_unframed_pdu) { - struct ble_ll_isoal_mux mux; + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; struct os_mbuf *sdu_1, *sdu_2; const uint32_t iso_interval_us = 20000; const uint32_t sdu_interval_us = 10000; @@ -170,70 +283,71 @@ TEST_CASE_SELF(test_ble_ll_isoal_mux_get_unframed_pdu) { uint8_t llid = 0x00; int rc; - ble_ll_isoal_mux_init(&mux, max_pdu, iso_interval_us, sdu_interval_us, bn, - 0, Framed, Framing_Mode); + test_ll_isoal_setup(&fixture, sdu_len, max_pdu, iso_interval_us, + sdu_interval_us, bn, Framed, Framing_Mode); /* SDU #1 */ - sdu_1 = os_mbuf_get_pkthdr(&os_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + sdu_1 = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); TEST_ASSERT_FATAL(sdu_1 != NULL); - rc = os_mbuf_append(sdu_1, os_mbuf_test_data, sdu_len); + rc = os_mbuf_append(sdu_1, g_test_sdu_data, sdu_len); TEST_ASSERT_FATAL(rc == 0); - ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_1); + ble_ll_isoal_mux_sdu_put(mux, sdu_1); /* SDU #2 */ - sdu_2 = os_mbuf_get_pkthdr(&os_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + sdu_2 = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); TEST_ASSERT_FATAL(sdu_2 != NULL); - rc = os_mbuf_append(sdu_2, os_mbuf_test_data, sdu_len); + rc = os_mbuf_append(sdu_2, g_test_sdu_data, sdu_len); TEST_ASSERT_FATAL(rc == 0); - ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_2); + ble_ll_isoal_mux_sdu_put(mux, sdu_2); - ble_ll_isoal_mux_event_start(&mux, 90990); + ble_ll_isoal_mux_event_start(mux, 90990); /* PDU #1 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; start or continuation fragment of an SDU. */ TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); /* PDU #2 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; start or continuation fragment of an SDU. */ TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); /* PDU #3 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 2, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 2, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; end fragment of an SDU or a complete SDU. */ TEST_ASSERT(llid == 0b00, "LLID is incorrect %d", llid); /* PDU #4 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; start or continuation fragment of an SDU. */ TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); /* PDU #5 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; start or continuation fragment of an SDU. */ TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); /* PDU #6 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 2, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 2, &llid, data); TEST_ASSERT(pdu_len == max_pdu, "PDU length is incorrect %d", pdu_len); /* Unframed CIS Data PDU; end fragment of an SDU or a complete SDU. */ TEST_ASSERT(llid == 0b00, "LLID is incorrect %d", llid); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt > 0, "num_completed_pkt is incorrect %d", num_completed_pkt); - ble_ll_isoal_mux_free(&mux); + test_ll_isoal_teardown(&fixture); } TEST_CASE_SELF(test_ble_ll_isoal_mux_sdu_not_in_event) { - struct ble_ll_isoal_mux mux; + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; struct os_mbuf *sdu_1; const uint32_t iso_interval_us = 10000; const uint32_t sdu_interval_us = 10000; @@ -248,38 +362,36 @@ TEST_CASE_SELF(test_ble_ll_isoal_mux_sdu_not_in_event) { uint8_t llid = 0x00; int rc; - ble_ll_isoal_mux_init(&mux, max_pdu, iso_interval_us, sdu_interval_us, bn, - 0, Framed, Framing_Mode); + test_ll_isoal_setup(&fixture, sdu_len, max_pdu, iso_interval_us, + sdu_interval_us, bn, Framed, Framing_Mode); - ble_ll_isoal_mux_event_start(&mux, 90990); - TEST_ASSERT_FATAL(mux.sdu_in_event == 0, - "sdu_in_event %d != 0", mux.sdu_in_event); + ble_ll_isoal_mux_event_start(mux, 90990); + TEST_ASSERT_FATAL(mux->sdu_in_event == 0, "sdu_in_event %d != 0", mux->sdu_in_event); /* SDU #1 */ - sdu_1 = os_mbuf_get_pkthdr(&os_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + sdu_1 = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); TEST_ASSERT_FATAL(sdu_1 != NULL); - rc = os_mbuf_append(sdu_1, os_mbuf_test_data, sdu_len); + rc = os_mbuf_append(sdu_1, g_test_sdu_data, sdu_len); TEST_ASSERT_FATAL(rc == 0); - ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_1); + ble_ll_isoal_mux_sdu_put(mux, sdu_1); - TEST_ASSERT_FATAL(mux.sdu_in_event == 0, - "sdu_in_event %d != 0", mux.sdu_in_event); + TEST_ASSERT_FATAL(mux->sdu_in_event == 0, "sdu_in_event %d != 0", mux->sdu_in_event); /* PDU #1 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, data); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); /* PDU #2 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, data); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, data); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == 0, "num_completed_pkt is incorrect %d", num_completed_pkt); - ble_ll_isoal_mux_free(&mux); + test_ll_isoal_teardown(&fixture); } static int @@ -295,7 +407,7 @@ test_sdu_enqueue(struct ble_ll_isoal_mux *mux, uint16_t sdu_len, TEST_ASSERT_FATAL(sdu_len <= TSPX_max_sdu_length, "incorrect sdu length"); - sdu = os_mbuf_get_pkthdr(&os_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + sdu = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); TEST_ASSERT_FATAL(sdu != NULL); blehdr = BLE_MBUF_HDR_PTR(sdu); blehdr->txiso.packet_seq_num = packet_seq_num; @@ -303,19 +415,19 @@ test_sdu_enqueue(struct ble_ll_isoal_mux *mux, uint16_t sdu_len, /* First SDU Fragment */ sdu_frag_len = min(sdu_len, HCI_iso_sdu_max); - rc = os_mbuf_append(sdu, os_mbuf_test_data, sdu_frag_len); + rc = os_mbuf_append(sdu, g_test_sdu_data, sdu_frag_len); TEST_ASSERT_FATAL(rc == 0); offset += sdu_frag_len; num_pkt++; while (offset < sdu_len) { - frag = os_mbuf_get_pkthdr(&os_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + frag = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); TEST_ASSERT_FATAL(frag != NULL); /* Subsequent SDU Fragments */ sdu_frag_len = min(sdu_len - offset, HCI_iso_sdu_max); - rc = os_mbuf_append(sdu, &os_mbuf_test_data[offset], sdu_frag_len); + rc = os_mbuf_append(sdu, &g_test_sdu_data[offset], sdu_frag_len); TEST_ASSERT_FATAL(rc == 0); offset += sdu_frag_len; @@ -324,18 +436,18 @@ test_sdu_enqueue(struct ble_ll_isoal_mux *mux, uint16_t sdu_len, os_mbuf_concat(sdu, frag); } - ble_ll_isoal_mux_sdu_enqueue(mux, sdu); + ble_ll_isoal_mux_sdu_put(mux, sdu); return num_pkt; } static void -test_pdu_verify(uint8_t *pdu, int pdu_len, uint16_t sdu_offset) +test_data_verify(uint8_t *pdu, int pdu_len, uint16_t sdu_offset) { for (int i = 0; i < pdu_len; i++) { - TEST_ASSERT(pdu[i] == os_mbuf_test_data[sdu_offset + i], - "PDU verification failed pdu[%d] %d != %d", - i, pdu[i], os_mbuf_test_data[sdu_offset + i]); + TEST_ASSERT(pdu[i] == g_test_sdu_data[sdu_offset + i], + "PDU verification failed pdu[%d] %d != %d", i, pdu[i], + g_test_sdu_data[sdu_offset + i]); } } @@ -350,29 +462,11 @@ struct test_ial_broadcast_single_sdu_bis_cfg { uint32_t ISO_Interval; }; -static void -test_ial_teardown(struct ble_ll_isoal_mux *mux) -{ - ble_ll_isoal_mux_free(mux); - TEST_ASSERT_FATAL(os_mbuf_mempool.mp_block_size == MBUF_TEST_POOL_BUF_SIZE, - "mp_block_size is %d", os_mbuf_mempool.mp_block_size); - TEST_ASSERT_FATAL(os_mbuf_mempool.mp_num_free == MBUF_TEST_POOL_BUF_COUNT, - "mp_num_free is %d", os_mbuf_mempool.mp_num_free); -} - -static void -test_ial_setup(struct ble_ll_isoal_mux *mux, uint8_t max_pdu, - uint32_t iso_interval_us, uint32_t sdu_interval_us, - uint8_t bn, uint8_t pte, bool framed, uint8_t framing_mode) -{ - ble_ll_isoal_mux_init(mux, max_pdu, iso_interval_us, sdu_interval_us, - bn, pte, framed, framing_mode); -} - static void test_ial_broadcast_single_sdu_bis(const struct test_ial_broadcast_single_sdu_bis_cfg *cfg) { - struct ble_ll_isoal_mux mux; + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; int num_completed_pkt; int pdu_len; uint32_t timeoffset; @@ -381,17 +475,16 @@ test_ial_broadcast_single_sdu_bis(const struct test_ial_broadcast_single_sdu_bis uint8_t pdu[cfg->Max_PDU]; uint8_t llid = 0xff; - test_ial_setup(&mux, cfg->Max_PDU, cfg->ISO_Interval, - cfg->SDU_Interval, cfg->BN, 0, cfg->Framed, - cfg->Framing_Mode); + test_ll_isoal_setup(&fixture, Max_SDU, cfg->Max_PDU, cfg->ISO_Interval, + cfg->SDU_Interval, cfg->BN, cfg->Framed, cfg->Framing_Mode); /* Send Single SDU */ - test_sdu_enqueue(&mux, Max_SDU, 0, 20000); + test_sdu_enqueue(mux, Max_SDU, 0, 20000); - ble_ll_isoal_mux_event_start(&mux, 30500); + ble_ll_isoal_mux_event_start(mux, 30500); /* PDU #1 */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == cfg->LLID, "LLID is incorrect %d", llid); if (cfg->Framed) { @@ -407,17 +500,17 @@ test_ial_broadcast_single_sdu_bis(const struct test_ial_broadcast_single_sdu_bis timeoffset = get_le24(&pdu[2]); TEST_ASSERT(timeoffset == 10500, "Time offset is incorrect %d", timeoffset); - test_pdu_verify(&pdu[5], Max_SDU, 0); + test_data_verify(&pdu[5], Max_SDU, 0); } else { TEST_ASSERT(pdu_len == Max_SDU, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[0], Max_SDU, 0); + test_data_verify(&pdu[0], Max_SDU, 0); } - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt > 0, "num_completed_pkt is incorrect %d", num_completed_pkt); - test_ial_teardown(&mux); + test_ll_isoal_teardown(&fixture); } TEST_CASE_SELF(test_ial_bis_unf_brd_bv_01_c) { @@ -515,8 +608,9 @@ test_ial_broadcast_large_sdu_bis(const struct test_ial_broadcast_large_sdu_bis_c {.sdu_len = 495, .sc_packets_num = 1}, {.sdu_len = 503, .sc_packets_num = 2}, }; - struct ble_ll_isoal_mux mux; - /* const uint16_t Max_SDU = 503; */ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; + const uint16_t Max_SDU = 503; const uint8_t Max_PDU = 251; int num_completed_pkt; int num_expected_pkt; @@ -530,9 +624,8 @@ test_ial_broadcast_large_sdu_bis(const struct test_ial_broadcast_large_sdu_bis_c uint8_t seg_len; uint8_t idx; - test_ial_setup(&mux, Max_PDU, cfg->ISO_Interval, - cfg->SDU_Interval, cfg->BN, 0, cfg->Framed, - cfg->Framing_Mode); + test_ll_isoal_setup(&fixture, Max_SDU, Max_PDU, cfg->ISO_Interval, + cfg->SDU_Interval, cfg->BN, cfg->Framed, cfg->Framing_Mode); for (size_t round = 0; round < ARRAY_SIZE(rounds); round++) { sc_packets_num = 0; @@ -540,12 +633,13 @@ test_ial_broadcast_large_sdu_bis(const struct test_ial_broadcast_large_sdu_bis_c timestamp = (round + 1) * cfg->SDU_Interval; - num_expected_pkt = test_sdu_enqueue(&mux, rounds[round].sdu_len, round, timestamp); + num_expected_pkt = + test_sdu_enqueue(mux, rounds[round].sdu_len, round, timestamp); - ble_ll_isoal_mux_event_start(&mux, timestamp + 100); + ble_ll_isoal_mux_event_start(mux, timestamp + 100); for (idx = 0; idx < cfg->BN; idx++) { - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, idx, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, idx, &llid, pdu); if (pdu_len == 0) { TEST_ASSERT_FATAL(sdu_offset == rounds[round].sdu_len, "Round #%d: idx %d sdu_offset %d", @@ -573,20 +667,20 @@ test_ial_broadcast_large_sdu_bis(const struct test_ial_broadcast_large_sdu_bis_c "Round #%d: SC is incorrect %d", round, BLE_LL_ISOAL_SEGHDR_SC(seg_hdr)); - test_pdu_verify(&pdu[5], seg_len - 3, 0); + test_data_verify(&pdu[5], seg_len - 3, 0); sdu_offset += seg_len - 3; } else { TEST_ASSERT_FATAL(BLE_LL_ISOAL_SEGHDR_SC(seg_hdr) == 1, "Round #%d: SC is incorrect %d", round, BLE_LL_ISOAL_SEGHDR_SC(seg_hdr)); - test_pdu_verify(&pdu[2], seg_len, sdu_offset); + test_data_verify(&pdu[2], seg_len, sdu_offset); sdu_offset += seg_len; } } else { TEST_ASSERT_FATAL(llid == 0b01, "Round #%d: LLID is incorrect %d", round, llid); - test_pdu_verify(&pdu[0], pdu_len, sdu_offset); + test_data_verify(&pdu[0], pdu_len, sdu_offset); sdu_offset += pdu_len; } @@ -612,7 +706,7 @@ test_ial_broadcast_large_sdu_bis(const struct test_ial_broadcast_large_sdu_bis_c round, BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr)); seg_len = BLE_LL_ISOAL_SEGHDR_LEN(seg_hdr); - test_pdu_verify(&pdu[2], seg_len, sdu_offset); + test_data_verify(&pdu[2], seg_len, sdu_offset); sdu_offset += seg_len; } else { TEST_ASSERT_FATAL(pdu_len == rounds[round].sdu_len - sdu_offset, @@ -620,18 +714,18 @@ test_ial_broadcast_large_sdu_bis(const struct test_ial_broadcast_large_sdu_bis_c round, idx, pdu_len, rounds[round].sdu_len - sdu_offset); TEST_ASSERT_FATAL(llid == 0b00, "Round #%d: LLID is incorrect %d", round, llid); - test_pdu_verify(&pdu[0], pdu_len, sdu_offset); + test_data_verify(&pdu[0], pdu_len, sdu_offset); sdu_offset += pdu_len; } } } - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == num_expected_pkt, "num_completed_pkt %d != %d", num_completed_pkt, num_expected_pkt); } - test_ial_teardown(&mux); + test_ll_isoal_teardown(&fixture); } TEST_CASE_SELF(test_ial_bis_unf_brd_bv_09_c) { @@ -710,8 +804,9 @@ struct test_ial_broadcast_multiple_small_sdus_bis_cfg { static void test_ial_broadcast_multiple_small_sdus_bis(const struct test_ial_broadcast_multiple_small_sdus_bis_cfg *cfg) { - struct ble_ll_isoal_mux mux; - /* const uint16_t Max_SDU = 25; */ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; + const uint16_t Max_SDU = 25; const uint8_t LLID = 0b10; const uint8_t Framed = 0x01; const uint8_t Framing_Mode = 0; @@ -724,25 +819,24 @@ test_ial_broadcast_multiple_small_sdus_bis(const struct test_ial_broadcast_multi uint8_t seg_len; uint8_t *seg; - test_ial_setup(&mux, cfg->Max_PDU, cfg->ISO_Interval, - cfg->SDU_Interval, cfg->BN, 0, Framed, - Framing_Mode); + test_ll_isoal_setup(&fixture, Max_SDU, cfg->Max_PDU, cfg->ISO_Interval, + cfg->SDU_Interval, cfg->BN, Framed, Framing_Mode); /* The Upper Tester sends to the IUT a small SDU1 with data length of 20 bytes. */ sdu_1_ts = 100; - test_sdu_enqueue(&mux, 20, 0, sdu_1_ts); + test_sdu_enqueue(mux, 20, 0, sdu_1_ts); /* The Upper Tester sends to the IUT a small SDU2 with data length of 25 bytes. */ sdu_2_ts = sdu_1_ts + cfg->SDU_Interval; - test_sdu_enqueue(&mux, 25, 0, sdu_2_ts); + test_sdu_enqueue(mux, 25, 0, sdu_2_ts); event_ts = sdu_2_ts + 200; - ble_ll_isoal_mux_event_start(&mux, event_ts); + ble_ll_isoal_mux_event_start(mux, event_ts); /* The IUT sends a single Broadcast ISO Data PDU with SDU1 followed by SDU2 over the BIS. * Each SDU header has SC = 0 and CMPT = 1. */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == LLID, "LLID is incorrect %d", llid); /* SDU 1 */ @@ -773,9 +867,9 @@ test_ial_broadcast_multiple_small_sdus_bis(const struct test_ial_broadcast_multi TEST_ASSERT(timeoffset == event_ts - sdu_2_ts, "Time offset is incorrect %d", timeoffset); - (void)ble_ll_isoal_mux_event_done(&mux); + (void)ble_ll_isoal_mux_event_done(mux); - test_ial_teardown(&mux); + test_ll_isoal_teardown(&fixture); } TEST_CASE_SELF(test_ial_bis_fra_brd_bv_17_c) { @@ -832,10 +926,11 @@ struct test_ial_broadcast_zero_length_sdu_bis_cfg { static void test_ial_broadcast_zero_length_sdu_bis(const struct test_ial_broadcast_zero_length_sdu_bis_cfg *cfg) { - struct ble_ll_isoal_mux mux; + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; const uint32_t ISO_Interval = 10000; const uint32_t SDU_Interval = 10000; - /* const uint16_t Max_SDU = 32; */ + const uint16_t Max_SDU = 32; const uint16_t Max_PDU = 32; int pdu_len; uint8_t pdu[Max_PDU]; @@ -843,13 +938,13 @@ test_ial_broadcast_zero_length_sdu_bis(const struct test_ial_broadcast_zero_leng uint16_t seg_hdr; uint8_t llid = 0xff; - test_ial_setup(&mux, Max_PDU, ISO_Interval, SDU_Interval, - cfg->BN, 0, cfg->Framed, cfg->Framing_Mode); + test_ll_isoal_setup(&fixture, Max_SDU, Max_PDU, ISO_Interval, SDU_Interval, + cfg->BN, cfg->Framed, cfg->Framing_Mode); /* The Upper Tester sends an HCI ISO Data packet to the IUT with zero data length. */ - test_sdu_enqueue(&mux, 0, 0, 100); + test_sdu_enqueue(mux, 0, 0, 100); - ble_ll_isoal_mux_event_start(&mux, 500); + ble_ll_isoal_mux_event_start(mux, 500); /* The IUT sends a single Broadcast ISO Data PDU with the LLID, * Framed, Framing_Mode, the segmentation header and time offset @@ -857,7 +952,7 @@ test_ial_broadcast_zero_length_sdu_bis(const struct test_ial_broadcast_zero_leng * and is 5 (Segmentation Header + TimeOffset) if LLID is 0b10. * SDU field is empty.. */ - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == cfg->LLID, "LLID is incorrect %d", llid); if (cfg->LLID == 0b00) { @@ -879,14 +974,14 @@ test_ial_broadcast_zero_length_sdu_bis(const struct test_ial_broadcast_zero_leng } for (uint8_t idx = 1; idx < cfg->BN; idx++) { - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, idx, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, idx, &llid, pdu); TEST_ASSERT(llid == cfg->LLID, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); } - (void)ble_ll_isoal_mux_event_done(&mux); + (void)ble_ll_isoal_mux_event_done(mux); - test_ial_teardown(&mux); + test_ll_isoal_teardown(&fixture); } TEST_CASE_SELF(test_ial_bis_unf_brd_bv_21_c) { @@ -1031,26 +1126,27 @@ struct test_ial_unframed_empty_pdus_with_llid_0b01_cfg { static void test_ial_unframed_empty_pdus_with_llid_0b01(const struct test_ial_unframed_empty_pdus_with_llid_0b01_cfg *cfg) { - struct ble_ll_isoal_mux mux; + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; int pdu_len; uint8_t pdu[cfg->mx_pdu]; uint32_t timestamp; uint8_t llid = 0xff; - ble_ll_isoal_mux_init(&mux, cfg->mx_pdu, cfg->iso_int, cfg->sdu_int, - cfg->bn, 0, false, 0); + test_ll_isoal_setup(&fixture, cfg->mx_sdu, cfg->mx_pdu, cfg->iso_int, + cfg->sdu_int, cfg->bn, false, 0); for (uint16_t sdu_len = 4; sdu_len < cfg->mx_sdu; sdu_len++) { timestamp = sdu_len * cfg->sdu_int; - test_sdu_enqueue(&mux, sdu_len, sdu_len, timestamp); + test_sdu_enqueue(mux, sdu_len, sdu_len, timestamp); - ble_ll_isoal_mux_event_start(&mux, timestamp + 50); + ble_ll_isoal_mux_event_start(mux, timestamp + 50); /* As the mx_sdu == mx_pdu, the data will always fit the single PDU */ TEST_ASSERT(cfg->mx_sdu == cfg->mx_pdu, "#%d: SDU and PDU length should be same", sdu_len); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == 0b00, "#%d: LLID is incorrect %d", sdu_len, llid); TEST_ASSERT(pdu_len == sdu_len, @@ -1058,7 +1154,7 @@ test_ial_unframed_empty_pdus_with_llid_0b01(const struct test_ial_unframed_empty /* Padding */ for (uint8_t idx = 1; idx < cfg->bn; idx++) { - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, idx, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, idx, &llid, pdu); TEST_ASSERT(llid == 0b01, "#%d #%d: LLID is incorrect %d", sdu_len, idx, llid); TEST_ASSERT(pdu_len == 0, @@ -1066,10 +1162,10 @@ test_ial_unframed_empty_pdus_with_llid_0b01(const struct test_ial_unframed_empty sdu_len, idx, pdu_len); } - (void)ble_ll_isoal_mux_event_done(&mux); + (void)ble_ll_isoal_mux_event_done(mux); } - ble_ll_isoal_mux_free(&mux); + test_ll_isoal_teardown(&fixture); } TEST_CASE_SELF(test_ial_bis_unf_brd_bv_29_c) { @@ -1088,10 +1184,11 @@ TEST_CASE_SELF(test_ial_bis_unf_brd_bv_29_c) { } TEST_CASE_SELF(test_ial_bis_unf_early_sdus) { - struct ble_ll_isoal_mux mux; + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; const uint32_t sdu_int = 7500; const uint32_t iso_int = 7500; - /* const uint16_t mx_sdu = 40; */ + const uint16_t mx_sdu = 40; const uint8_t mx_pdu = 40; const uint8_t bn = 4; int num_completed_pkt; @@ -1100,86 +1197,87 @@ TEST_CASE_SELF(test_ial_bis_unf_early_sdus) { uint32_t timestamp = 0; uint8_t llid = 0xff; - test_ial_setup(&mux, mx_pdu, iso_int, sdu_int, bn, 0, false, 0); + test_ll_isoal_setup(&fixture, mx_sdu, mx_pdu, iso_int, sdu_int, bn, false, 0); - test_sdu_enqueue(&mux, 21, 0, timestamp++); - test_sdu_enqueue(&mux, 32, 0, timestamp++); - test_sdu_enqueue(&mux, 40, 0, timestamp++); + test_sdu_enqueue(mux, 21, 0, timestamp++); + test_sdu_enqueue(mux, 32, 0, timestamp++); + test_sdu_enqueue(mux, 40, 0, timestamp++); - ble_ll_isoal_mux_event_start(&mux, timestamp + 50); + ble_ll_isoal_mux_event_start(mux, timestamp + 50); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == 0b00, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 21, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(pdu, pdu_len, 0); + test_data_verify(pdu, pdu_len, 0); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, pdu); TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 2, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 2, &llid, pdu); TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 3, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 3, &llid, pdu); TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == 1, "num_completed_pkt is incorrect %d", num_completed_pkt); - ble_ll_isoal_mux_event_start(&mux, timestamp + 50 + iso_int); + ble_ll_isoal_mux_event_start(mux, timestamp + 50 + iso_int); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == 0b00, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 32, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(pdu, pdu_len, 0); + test_data_verify(pdu, pdu_len, 0); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, pdu); TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 2, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 2, &llid, pdu); TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 3, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 3, &llid, pdu); TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == 1, "num_completed_pkt is incorrect %d", num_completed_pkt); - ble_ll_isoal_mux_event_start(&mux, timestamp + 50 + 2 * iso_int); + ble_ll_isoal_mux_event_start(mux, timestamp + 50 + 2 * iso_int); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == 0b00, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 40, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(pdu, pdu_len, 0); + test_data_verify(pdu, pdu_len, 0); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, pdu); TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 2, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 2, &llid, pdu); TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 3, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 3, &llid, pdu); TEST_ASSERT(llid == 0b01, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == 1, "num_completed_pkt is incorrect %d", num_completed_pkt); - test_ial_teardown(&mux); + test_ll_isoal_teardown(&fixture); } TEST_CASE_SELF(test_ial_bis_fra_early_sdus) { - struct ble_ll_isoal_mux mux; + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_mux *mux = &fixture.mux; const uint32_t sdu_int = 87072; const uint32_t iso_int = 87500; const uint16_t mx_sdu = 32; @@ -1191,142 +1289,1970 @@ TEST_CASE_SELF(test_ial_bis_fra_early_sdus) { uint32_t timestamp = 0; uint8_t llid = 0xff; - test_ial_setup(&mux, mx_pdu, iso_int, sdu_int, bn, 0, true, 0); + test_ll_isoal_setup(&fixture, mx_sdu, mx_pdu, iso_int, sdu_int, bn, true, 0); for (int seq_num = 0; seq_num < 10; seq_num++) { - test_sdu_enqueue(&mux, mx_sdu, seq_num, timestamp++); + test_sdu_enqueue(mux, mx_sdu, seq_num, timestamp++); } - ble_ll_isoal_mux_event_start(&mux, timestamp); + ble_ll_isoal_mux_event_start(mux, timestamp); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == 2, "num_completed_pkt is incorrect %d", num_completed_pkt); - ble_ll_isoal_mux_event_start(&mux, timestamp); + ble_ll_isoal_mux_event_start(mux, timestamp); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == 2, "num_completed_pkt is incorrect %d", num_completed_pkt); - ble_ll_isoal_mux_event_start(&mux, timestamp); + ble_ll_isoal_mux_event_start(mux, timestamp); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == 2, "num_completed_pkt is incorrect %d", num_completed_pkt); - ble_ll_isoal_mux_event_start(&mux, timestamp); + ble_ll_isoal_mux_event_start(mux, timestamp); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == 2, "num_completed_pkt is incorrect %d", num_completed_pkt); - ble_ll_isoal_mux_event_start(&mux, timestamp); + ble_ll_isoal_mux_event_start(mux, timestamp); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 0, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 0, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - pdu_len = ble_ll_isoal_mux_pdu_get(&mux, 1, &llid, pdu); + pdu_len = ble_ll_isoal_mux_pdu_get(mux, 1, &llid, pdu); TEST_ASSERT(llid == 0b10, "LLID is incorrect %d", llid); TEST_ASSERT(pdu_len == mx_pdu, "PDU length is incorrect %d", pdu_len); - test_pdu_verify(&pdu[5], mx_sdu, 0); + test_data_verify(&pdu[5], mx_sdu, 0); - num_completed_pkt = ble_ll_isoal_mux_event_done(&mux); + num_completed_pkt = ble_ll_isoal_mux_event_done(mux); TEST_ASSERT(num_completed_pkt == 2, "num_completed_pkt is incorrect %d", num_completed_pkt); - test_ial_teardown(&mux); + test_ll_isoal_teardown(&fixture); } -TEST_SUITE(ble_ll_isoal_test_suite) { - os_mbuf_test_setup(); +static void +test_data_verify_om(struct os_mbuf *om, uint16_t len) +{ + struct os_mbuf *om_next; + uint16_t offset; + uint16_t dlen; - ble_ll_isoal_init(); + offset = 0; - test_ble_ll_isoal_mux_init(); - test_ble_ll_isoal_mux_get_unframed_pdu(); - test_ble_ll_isoal_mux_sdu_not_in_event(); + while (offset < len) { + dlen = min(len - offset, om->om_len); + test_data_verify(om->om_data, dlen, offset); + os_mbuf_adj(om, dlen); + offset += dlen; - /* Broadcast Single SDU, BIS */ - test_ial_bis_unf_brd_bv_01_c(); - test_ial_bis_unf_brd_bv_02_c(); - test_ial_bis_fra_brd_bv_06_c(); - test_ial_bis_fra_brd_bv_08_c(); - test_ial_bis_fra_brd_bv_29_c(); + if (om->om_len == 0) { + om_next = SLIST_NEXT(om, om_next); + om = om_next; + } + } +} - /* Broadcast Large SDU, BIS */ - test_ial_bis_unf_brd_bv_09_c(); - test_ial_bis_unf_brd_bv_10_c(); - test_ial_bis_unf_brd_bv_11_c(); - test_ial_bis_fra_brd_bv_13_c(); - test_ial_bis_fra_brd_bv_15_c(); +static struct os_mbuf * +test_ial_lt_pdu_get(uint8_t llid, uint8_t **payload_len) +{ + struct ble_mbuf_hdr *blehdr; + struct os_mbuf *om; + uint32_t ticks = 0; + uint8_t rem_us = 0; + uint8_t *pdu_hdr; - /* Broadcast Multiple, Small SDUs, BIS */ - test_ial_bis_fra_brd_bv_17_c(); - test_ial_bis_fra_brd_bv_18_c(); - test_ial_bis_fra_brd_bv_20_c(); + om = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + TEST_ASSERT_FATAL(om != NULL); - /* Broadcast a Zero-Length SDU, BIS */ - test_ial_bis_unf_brd_bv_21_c(); - test_ial_bis_unf_brd_bv_22_c(); - test_ial_bis_unf_brd_bv_23_c(); - test_ial_bis_unf_brd_bv_24_c(); - test_ial_bis_fra_brd_bv_25_c(); - test_ial_bis_fra_brd_bv_26_c(); - test_ial_bis_fra_brd_bv_27_c(); - test_ial_bis_fra_brd_bv_28_c(); - test_ial_bis_fra_brd_bv_30_c(); + blehdr = BLE_MBUF_HDR_PTR(om); + blehdr->beg_cputime = ticks; + blehdr->rem_usecs = rem_us; - /* Broadcasting Unframed Empty PDUs with LLID=0b01, BIS */ - test_ial_bis_unf_brd_bv_29_c(); - /* test_ial_bis_unf_brd_bv_30_c(); - * Same as test_ial_bis_unf_brd_bv_29_c except encryption is required. - */ + pdu_hdr = os_mbuf_extend(om, BLE_LL_PDU_HDR_LEN); + TEST_ASSERT_FATAL(pdu_hdr != NULL); + + pdu_hdr[0] = llid; + pdu_hdr[1] = 0; + + *payload_len = &pdu_hdr[1]; + + return om; +} + +static void +test_ial_lt_pdu_seghdr_put(struct os_mbuf *om, uint8_t *payload_len, bool start, + bool cmplt, uint32_t time_offset, uint8_t **seg_len) +{ + uint32_t *timeoffset; + uint8_t *seg_hdr; + + /* Segmentation Header */ + *payload_len += 2; + seg_hdr = os_mbuf_extend(om, 2); + TEST_ASSERT_FATAL(seg_hdr != NULL); + + if (start) { + /* TimeOffset */ + *payload_len += 3; + timeoffset = os_mbuf_extend(om, 3); + TEST_ASSERT_FATAL(timeoffset != NULL); + put_le24(timeoffset, time_offset); + } + + seg_hdr[0] = (start ? 0b00 : 0b01) | (cmplt ? 0b10 : 0b00); + /* Length, including TimeOffset if present */ + seg_hdr[1] = start ? 3 : 0; + + *seg_len = &seg_hdr[1]; +} + +static uint8_t +test_ial_lt_pdu_data_put(struct os_mbuf *om, uint8_t *payload_len, + const void *data, uint8_t dlen) +{ + int rc; + + TEST_ASSERT_FATAL(dlen + *payload_len <= UINT8_MAX); + + rc = os_mbuf_append(om, data, dlen); + TEST_ASSERT_FATAL(rc == 0); + + *payload_len += dlen; + + return dlen; +} + +static void +test_ial_lt_pdu_send(struct os_mbuf *om, uint8_t idx, uint8_t *payload_len, + struct ble_ll_isoal_demux *demux) +{ + ble_ll_isoal_demux_pdu_put(demux, idx, om); +} + +static uint8_t +test_ial_lt_unf_pdu_send(struct ble_ll_isoal_demux *demux, uint8_t idx, + const void *data, uint16_t dlen, uint8_t llid) +{ + struct os_mbuf *om; + uint8_t *payload_len; + + om = test_ial_lt_pdu_get(llid, &payload_len); + + if (dlen > demux->config.max_pdu) { + dlen = demux->config.max_pdu; + } + + test_ial_lt_pdu_data_put(om, payload_len, data, dlen); + test_ial_lt_pdu_send(om, idx, payload_len, demux); + + return *payload_len; +} + +static uint8_t +test_ial_lt_fra_pdu_send(struct ble_ll_isoal_demux *demux, uint8_t idx, + const void *data, uint16_t dlen, uint32_t time_offset, + bool start, bool cmplt) +{ + struct os_mbuf *om; + uint8_t *payload_len; + uint8_t *seg_len; + + om = test_ial_lt_pdu_get(0b10, &payload_len); + test_ial_lt_pdu_seghdr_put(om, payload_len, start, cmplt, time_offset, &seg_len); + + if (dlen + *payload_len > demux->config.max_pdu) { + dlen = demux->config.max_pdu - *payload_len; + } + + dlen = test_ial_lt_pdu_data_put(om, payload_len, data, dlen); + *seg_len += dlen; + + test_ial_lt_pdu_send(om, idx, payload_len, demux); + + return dlen; +} + +static void +test_ial_lt_send_padding(struct ble_ll_isoal_demux *demux, uint8_t idx, bool framed) +{ + struct os_mbuf *pdu; + uint8_t *payload_len; + uint8_t llid; + + llid = framed ? BLE_LL_BIS_LLID_DATA_PDU_FRAMED + : BLE_LL_BIS_LLID_DATA_PDU_UNFRAMED_SC; + + pdu = test_ial_lt_pdu_get(llid, &payload_len); + *payload_len = 0; + + test_ial_lt_pdu_send(pdu, idx, payload_len, demux); +} + +static void +test_ial_lt_fra_null_pdu_send(struct ble_ll_isoal_demux *demux, uint8_t idx) +{ + test_ial_lt_send_padding(demux, idx, true); +} + +static void +test_ial_sdu_verify(struct ble_ll_isoal_demux *demux, uint16_t sdu_len, uint8_t pkt_status) +{ + struct os_mbuf_pkthdr *pkthdr; + struct os_mbuf *om; + struct test_ial_userhdr *userhdr; + + pkthdr = STAILQ_FIRST(&sdu_q); + TEST_ASSERT_FATAL(pkthdr != NULL); + + om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + + userhdr = OS_MBUF_USRHDR(om); + TEST_ASSERT(userhdr->pkt_status == pkt_status); + + if (pkt_status == 0b00) { + /* Verify the SDU Length */ + TEST_ASSERT_FATAL(sdu_len == os_mbuf_len(om)); + + /* Verify the SDU contents */ + test_data_verify_om(om, os_mbuf_len(om)); + } + + os_mbuf_free_chain(om); + + STAILQ_REMOVE_HEAD(&sdu_q, omp_next); +} + +struct test_ial_receive_a_single_sdu_bis_cfg { + uint8_t Max_PDU; + uint8_t Max_SDU; + uint8_t NSE; + uint8_t Framed; + uint8_t Framing_Mode; + uint8_t LLID; + uint8_t BN; + uint32_t SDU_Interval; + uint32_t ISO_Interval; +}; + +static void +test_ial_receive_a_single_sdu_bis(struct test_ial_receive_a_single_sdu_bis_cfg *cfg) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + uint8_t sent; + + test_ll_isoal_setup(&fixture, cfg->Max_SDU, cfg->Max_PDU, cfg->ISO_Interval, + cfg->SDU_Interval, cfg->BN, cfg->Framed, cfg->Framing_Mode); + + ble_ll_isoal_demux_event_start(demux, cfg->ISO_Interval); + + if (cfg->Framed) { + sent = test_ial_lt_fra_pdu_send(demux, 0, &g_test_sdu_data[0], + cfg->Max_SDU, 4000u, true, true); + } else { + sent = test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[0], + cfg->Max_SDU, cfg->LLID); + } + + TEST_ASSERT_FATAL(sent == cfg->Max_SDU); + + ble_ll_isoal_demux_event_done(demux); + + test_ial_sdu_verify(demux, cfg->Max_SDU, 0b00); + + test_ll_isoal_teardown(&fixture); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bv_01_c) +{ + struct test_ial_receive_a_single_sdu_bis_cfg cfg = { + .Max_PDU = 40, + .Max_SDU = 32, + .NSE = 2, + .Framed = 0, + .Framing_Mode = 0, + .LLID = 0b00, + .BN = 2, + .SDU_Interval = 5000, + .ISO_Interval = 10000, + }; + + test_ial_receive_a_single_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bv_02_c) +{ + struct test_ial_receive_a_single_sdu_bis_cfg cfg = { + .Max_PDU = 40, + .Max_SDU = 32, + .NSE = 1, + .Framed = 0, + .Framing_Mode = 0, + .LLID = 0b00, + .BN = 1, + .SDU_Interval = 10000, + .ISO_Interval = 10000, + }; + + test_ial_receive_a_single_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bv_03_c) +{ + struct test_ial_receive_a_single_sdu_bis_cfg cfg = { + .Max_PDU = 40, + .Max_SDU = 32, + .NSE = 2, + .Framed = 0, + .Framing_Mode = 0, + .LLID = 0b00, + .BN = 2, + .SDU_Interval = 10000, + .ISO_Interval = 10000, + }; + + test_ial_receive_a_single_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_06_c) +{ + struct test_ial_receive_a_single_sdu_bis_cfg cfg = { + .Max_PDU = 42, + .Max_SDU = 32, + .NSE = 4, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 2, + .SDU_Interval = 5000, + .ISO_Interval = 10000, + }; + + test_ial_receive_a_single_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_08_c) +{ + struct test_ial_receive_a_single_sdu_bis_cfg cfg = { + .Max_PDU = 45, + .Max_SDU = 32, + .NSE = 2, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 1, + .SDU_Interval = 10000, + .ISO_Interval = 10000, + }; + + test_ial_receive_a_single_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_29_c) +{ + struct test_ial_receive_a_single_sdu_bis_cfg cfg = { + .Max_PDU = 3 * (32 + 5), + .Max_SDU = 32, + .NSE = 4, + .Framed = 1, + .Framing_Mode = 1, + .LLID = 0b10, + .BN = 1, + .SDU_Interval = 5000, + .ISO_Interval = 10000, + }; + + test_ial_receive_a_single_sdu_bis(&cfg); +} + +struct test_ial_receive_large_sdu_bis_round { + uint16_t sdu_len; + uint8_t sc_packets_num; +}; +struct test_ial_receive_large_sdu_bis_cfg { + uint8_t NSE; + uint8_t Framing; + uint8_t BN; + uint32_t SDU_Interval; + uint32_t ISO_Interval; + struct test_ial_receive_large_sdu_bis_round rounds[2]; +}; + +static void +test_ial_receive_large_sdu_bis(const struct test_ial_receive_large_sdu_bis_cfg *cfg) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + const uint8_t Max_PDU = 251; + uint16_t sdu_offset; + + test_ll_isoal_setup(&fixture, TSPX_max_sdu_length, Max_PDU, cfg->ISO_Interval, + cfg->SDU_Interval, cfg->BN, cfg->Framing, 0); + + for (uint8_t round = 0; round < ARRAY_SIZE(cfg->rounds); round++) { + TEST_ASSERT_FATAL(cfg->rounds[round].sc_packets_num + 1 <= cfg->BN); + + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, (round + 1) * cfg->ISO_Interval); + + for (uint8_t i = 0; i < cfg->BN; i++) { + if (i < cfg->rounds[round].sc_packets_num) { + /* 1. The Lower Tester sends the number of Start/Continuation packets */ + if (cfg->Framing) { + sdu_offset += test_ial_lt_fra_pdu_send( + demux, i, &g_test_sdu_data[sdu_offset], + cfg->rounds[round].sdu_len - sdu_offset, 100, + sdu_offset == 0, false); + } else { + sdu_offset += test_ial_lt_unf_pdu_send( + demux, i, &g_test_sdu_data[sdu_offset], + cfg->rounds[round].sdu_len - sdu_offset, 0b01); + } + } else if (i == cfg->rounds[round].sc_packets_num) { + /* 2. The Lower Tester sends the last ISO Data PDU, with the remaining Payload Data */ + if (cfg->Framing) { + sdu_offset += test_ial_lt_fra_pdu_send( + demux, i, &g_test_sdu_data[sdu_offset], + cfg->rounds[round].sdu_len - sdu_offset, + 0 /* ignored */, false, true); + } else { + sdu_offset += test_ial_lt_unf_pdu_send( + demux, i, &g_test_sdu_data[sdu_offset], + cfg->rounds[round].sdu_len - sdu_offset, 0b00); + } + } else { + /* Padding */ + test_ial_lt_send_padding(demux, i, cfg->Framing); + } + } + + TEST_ASSERT_FATAL(sdu_offset == cfg->rounds[round].sdu_len); + + ble_ll_isoal_demux_event_done(demux); + + /* Pass verdict */ + test_ial_sdu_verify(demux, cfg->rounds[round].sdu_len, 0b00); + } + + test_ll_isoal_teardown(&fixture); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bv_09_c) +{ + struct test_ial_receive_large_sdu_bis_cfg cfg = { + .NSE = 8, + .Framing = 0, + .BN = 4, + .SDU_Interval = 25000, + .ISO_Interval = 25000, + .rounds = { { .sdu_len = 753, .sc_packets_num = 2 }, + { .sdu_len = 754, .sc_packets_num = 3 } } + }; + + test_ial_receive_large_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bv_10_c) +{ + struct test_ial_receive_large_sdu_bis_cfg cfg = { + .NSE = 8, + .Framing = 0, + .BN = 4, + .SDU_Interval = 50000, + .ISO_Interval = 50000, + .rounds = { { .sdu_len = 753, .sc_packets_num = 2 }, + { .sdu_len = 754, .sc_packets_num = 3 } } + }; + + test_ial_receive_large_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_11_c) +{ + struct test_ial_receive_large_sdu_bis_cfg cfg = { + .NSE = 8, + .Framing = 1, + .BN = 4, + .SDU_Interval = 40000, + .ISO_Interval = 50000, + .rounds = { { .sdu_len = 744, .sc_packets_num = 2 }, + { .sdu_len = 745, .sc_packets_num = 3 } } + }; + + test_ial_receive_large_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_13_c) +{ + struct test_ial_receive_large_sdu_bis_cfg cfg = { + .NSE = 8, + .Framing = 1, + .BN = 4, + .SDU_Interval = 25000, + .ISO_Interval = 25000, + .rounds = { { .sdu_len = 744, .sc_packets_num = 2 }, + { .sdu_len = 745, .sc_packets_num = 3 } } + }; + + test_ial_receive_large_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_15_c) +{ + struct test_ial_receive_large_sdu_bis_cfg cfg = { + .NSE = 8, + .Framing = 1, + .BN = 4, + .SDU_Interval = 30000, + .ISO_Interval = 35000, + .rounds = { { .sdu_len = 744, .sc_packets_num = 2 }, + { .sdu_len = 745, .sc_packets_num = 3 } } + }; + + test_ial_receive_large_sdu_bis(&cfg); +} + +struct test_ial_receive_multiple_small_sdus_bis_cfg { + uint8_t NSE; + uint8_t BN; + uint32_t SDU_Interval; + uint32_t ISO_Interval; +}; + +static void +test_ial_receive_multiple_small_sdus_bis(struct test_ial_receive_multiple_small_sdus_bis_cfg *cfg) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + struct ble_mbuf_hdr *blehdr; + struct os_mbuf *pdu; + const uint8_t Max_PDU = 68; + uint32_t ticks = 0; + uint8_t rem_us = 0; + uint32_t *timeoffset; + uint16_t *pdu_hdr; + uint16_t *seg_hdr; + int rc; + + test_ll_isoal_setup(&fixture, TSPX_max_sdu_length, Max_PDU, + cfg->ISO_Interval, cfg->SDU_Interval, cfg->BN, 1, 0); + + seg_hdr = NULL; + timeoffset = NULL; + + ble_ll_isoal_demux_event_start(demux, cfg->ISO_Interval); + + pdu = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(*blehdr)); + TEST_ASSERT_FATAL(pdu != NULL); + + blehdr = BLE_MBUF_HDR_PTR(pdu); + blehdr->beg_cputime = ticks; + blehdr->rem_usecs = rem_us; + + pdu_hdr = os_mbuf_extend(pdu, sizeof(*pdu_hdr)); + TEST_ASSERT_FATAL(pdu_hdr != NULL); + + /** + * SDU1 with data length of 20 bytes + */ + + /* Segmentation Header */ + seg_hdr = os_mbuf_extend(pdu, sizeof(*seg_hdr)); + TEST_ASSERT_FATAL(seg_hdr != NULL); + put_le16(seg_hdr, BLE_LL_ISOAL_SEGHDR(false, true, 23)); + + /* TimeOffset */ + timeoffset = os_mbuf_extend(pdu, 3); + TEST_ASSERT_FATAL(timeoffset != NULL); + put_le24(timeoffset, cfg->ISO_Interval); + + rc = os_mbuf_append(pdu, g_test_sdu_data, 20); + TEST_ASSERT_FATAL(rc == 0); + + /** + * SDU2 with data length of 25 bytes + */ + + /* Segmentation Header */ + seg_hdr = os_mbuf_extend(pdu, sizeof(*seg_hdr)); + TEST_ASSERT_FATAL(seg_hdr != NULL); + put_le16(seg_hdr, BLE_LL_ISOAL_SEGHDR(false, true, 28)); + + /* TimeOffset */ + timeoffset = os_mbuf_extend(pdu, 3); + TEST_ASSERT_FATAL(timeoffset != NULL); + put_le24(timeoffset, cfg->ISO_Interval - cfg->SDU_Interval); + + rc = os_mbuf_append(pdu, g_test_sdu_data, 25); + TEST_ASSERT_FATAL(rc == 0); + + *pdu_hdr = 0b10 | ((os_mbuf_len(pdu) - sizeof(*pdu_hdr)) << 8); + + ble_ll_isoal_demux_pdu_put(demux, 0, pdu); + + ble_ll_isoal_demux_event_done(demux); + + /** + * Pass verdict + */ + + /* IUT sends an SDU to the Upper Tester with the data for SDU1 */ + test_ial_sdu_verify(demux, 20, 0b00); + + /* IUT sends an SDU to the Upper Tester with the data for SDU2 */ + test_ial_sdu_verify(demux, 25, 0b00); + + test_ll_isoal_teardown(&fixture); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_17_c) +{ + struct test_ial_receive_multiple_small_sdus_bis_cfg cfg = { + .NSE = 2, + .BN = 1, + .SDU_Interval = 5000, + .ISO_Interval = 10000, + }; + + test_ial_receive_multiple_small_sdus_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_18_c) +{ + struct test_ial_receive_multiple_small_sdus_bis_cfg cfg = { + .NSE = 2, + .BN = 2, + .SDU_Interval = 5000, + .ISO_Interval = 20000, + }; + + test_ial_receive_multiple_small_sdus_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_20_c) +{ + struct test_ial_receive_multiple_small_sdus_bis_cfg cfg = { + .NSE = 4, + .BN = 2, + .SDU_Interval = 5000, + .ISO_Interval = 20000, + }; + + test_ial_receive_multiple_small_sdus_bis(&cfg); +} + +struct test_ial_seg_hdr { + uint8_t SC; + uint8_t CMPLT; + uint8_t LENGTH; +}; +struct test_ial_receive_a_zero_length_sdu_bis_cfg { + uint8_t NSE; + uint8_t Framed; + uint8_t Framing_Mode; + uint8_t LLID; + uint8_t BN; + struct test_ial_seg_hdr *seg_hdr; + bool time_offset; +}; + +static void +test_ial_receive_a_zero_length_sdu_bis(struct test_ial_receive_a_zero_length_sdu_bis_cfg *cfg) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + struct ble_mbuf_hdr *blehdr; + struct os_mbuf *pdu; + const uint16_t ISO_Interval = 10000; + const uint16_t SDU_Interval = 10000; + const uint8_t Max_PDU = 32; + uint32_t ticks = 0; + uint8_t rem_us = 0; + uint32_t *timeoffset; + uint16_t *pdu_hdr; + uint16_t *seg_hdr; + + test_ll_isoal_setup(&fixture, TSPX_max_sdu_length, Max_PDU, ISO_Interval, + SDU_Interval, cfg->BN, cfg->Framed, cfg->Framing_Mode); + + ble_ll_isoal_demux_event_start(demux, ISO_Interval); + + seg_hdr = NULL; + timeoffset = NULL; + + pdu = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(*blehdr)); + TEST_ASSERT_FATAL(pdu != NULL); + + blehdr = BLE_MBUF_HDR_PTR(pdu); + blehdr->beg_cputime = ticks; + blehdr->rem_usecs = rem_us; + + pdu_hdr = os_mbuf_extend(pdu, sizeof(*pdu_hdr)); + TEST_ASSERT_FATAL(pdu_hdr != NULL); + + if (cfg->seg_hdr) { + /* Segmentation Header */ + seg_hdr = os_mbuf_extend(pdu, sizeof(*seg_hdr)); + TEST_ASSERT_FATAL(seg_hdr != NULL); + put_le16(seg_hdr, BLE_LL_ISOAL_SEGHDR(cfg->seg_hdr->SC, cfg->seg_hdr->CMPLT, + cfg->seg_hdr->LENGTH)); + } + + if (cfg->time_offset) { + /* TimeOffset */ + timeoffset = os_mbuf_extend(pdu, 3); + TEST_ASSERT_FATAL(timeoffset != NULL); + put_le24(timeoffset, 0); + } + + *pdu_hdr = cfg->LLID | ((os_mbuf_len(pdu) - sizeof(*pdu_hdr)) << 8); + + ble_ll_isoal_demux_pdu_put(demux, 0, pdu); + + /* Padding if needed */ + for (uint8_t i = 1; i < cfg->BN; i++) { + test_ial_lt_send_padding(demux, i, cfg->Framed); + } + + ble_ll_isoal_demux_event_done(demux); + + /** + * Pass verdict + */ + + /* IUT sends an empty SDU to the Upper Tester with the TimeOffset field */ + test_ial_sdu_verify(demux, 0, 0b00); + + test_ll_isoal_teardown(&fixture); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bv_21_c) +{ + struct test_ial_receive_a_zero_length_sdu_bis_cfg cfg = { + .NSE = 4, + .Framed = 0, + .Framing_Mode = 0, + .LLID = 0b00, + .BN = 2, + .seg_hdr = NULL, + }; + + test_ial_receive_a_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bv_22_c) +{ + struct test_ial_receive_a_zero_length_sdu_bis_cfg cfg = { + .NSE = 6, + .Framed = 0, + .Framing_Mode = 0, + .LLID = 0b00, + .BN = 3, + .seg_hdr = NULL, + }; + + test_ial_receive_a_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bv_23_c) +{ + struct test_ial_receive_a_zero_length_sdu_bis_cfg cfg = { + .NSE = 1, + .Framed = 0, + .Framing_Mode = 0, + .LLID = 0b00, + .BN = 1, + .seg_hdr = NULL, + }; + + test_ial_receive_a_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bv_24_c) +{ + struct test_ial_receive_a_zero_length_sdu_bis_cfg cfg = { + .NSE = 2, + .Framed = 0, + .Framing_Mode = 0, + .LLID = 0b00, + .BN = 1, + .seg_hdr = NULL, + }; + + test_ial_receive_a_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_25_c) +{ + struct test_ial_seg_hdr seg_hdr = { + .SC = 0, + .CMPLT = 1, + .LENGTH = 3, + }; + struct test_ial_receive_a_zero_length_sdu_bis_cfg cfg = { + .NSE = 6, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 2, + .seg_hdr = &seg_hdr, + .time_offset = true, + }; + + test_ial_receive_a_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_26_c) +{ + struct test_ial_seg_hdr seg_hdr = { + .SC = 0, + .CMPLT = 1, + .LENGTH = 3, + }; + struct test_ial_receive_a_zero_length_sdu_bis_cfg cfg = { + .NSE = 2, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 1, + .seg_hdr = &seg_hdr, + .time_offset = true, + }; + + test_ial_receive_a_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_27_c) +{ + struct test_ial_seg_hdr seg_hdr = { + .SC = 0, + .CMPLT = 1, + .LENGTH = 3, + }; + struct test_ial_receive_a_zero_length_sdu_bis_cfg cfg = { + .NSE = 4, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 1, + .seg_hdr = &seg_hdr, + .time_offset = true, + }; + + test_ial_receive_a_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_28_c) +{ + struct test_ial_seg_hdr seg_hdr = { + .SC = 0, + .CMPLT = 1, + .LENGTH = 3, + }; + struct test_ial_receive_a_zero_length_sdu_bis_cfg cfg = { + .NSE = 6, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 3, + .seg_hdr = &seg_hdr, + .time_offset = true, + }; + + test_ial_receive_a_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bv_30_c) +{ + struct test_ial_seg_hdr seg_hdr = { + .SC = 0, + .CMPLT = 1, + .LENGTH = 3, + }; + struct test_ial_receive_a_zero_length_sdu_bis_cfg cfg = { + .NSE = 6, + .Framed = 1, + .Framing_Mode = 1, + .LLID = 0b10, + .BN = 2, + .seg_hdr = &seg_hdr, + .time_offset = true, + }; + + test_ial_receive_a_zero_length_sdu_bis(&cfg); +} + +/** + * Send 4 PDUs with LLID=0b01 and no data + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_1(struct ble_ll_isoal_demux *demux) +{ + ble_ll_isoal_demux_event_start(demux, 0); + + test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[0], 0, 0b01); + test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[0], 0, 0b01); + test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[0], 0, 0b01); + test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[0], 0, 0b01); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall be reported as lost data */ + test_ial_sdu_verify(demux, 0, 0b10); +} + +/** + * Send 4 PDUs with LLID=0b01 and data + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_2(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as data with possible errors */ + test_ial_sdu_verify(demux, 0, 0b01); +} + +/** + * Send 3 PDUs with LLID=0b01 and data, then 1 PDU with LLID=0b00 and data; + * one of the first three PDUs has a CRC error + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_3(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + /* Since PDUs with CRC error are discarded, assume the one below is lost. */ + // sdu_offset += test_ial_lt_send_sdu_frag(demux, 1, &g_test_sdu_data[sdu_offset], + // sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as data with possible errors */ + test_ial_sdu_verify(demux, 0, 0b01); +} + +/** + * Send 2 PDUs with LLID=0b01 and data, then 2 PDUs with LLID=0b00 and data + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_4(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as data with possible errors */ + test_ial_sdu_verify(demux, sdu_offset, 0b01); +} + +/** + * Send 2 PDUs with LLID=0b01 and no data, then 1 PDU with LLID=0b00 and data, + * then 1 PDU with LLID=0b01 and data + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_5(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], 0, 0b01); + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], 0, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as data with possible errors */ + test_ial_sdu_verify(demux, sdu_offset, 0b01); +} + +/** + * Send 2 PDUs with LLID=0b01 and no data, then 1 PDU with LLID=0b00 and data, + * then 1 PDU with LLID=0b10 and no data. + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_6(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], 0, 0b01); + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], 0, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], 0, 0b10); + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as data with possible errors */ + test_ial_sdu_verify(demux, 0, 0b01); +} + +/** + * Send 2 PDUs with LLID=0b01 and no data, then 1 PDU with LLID=0b10 and data, + * then 1 PDU with LLID=0b00 and no data + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_7(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], 0, 0b01); + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], 0, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b10); + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], 0, 0b00); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall be reported as lost data */ + test_ial_sdu_verify(demux, 0, 0b10); +} + +static void +test_ial_bis_unf_snc_bi_02_c_round_8a(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + uint16_t incomplete_sdu_len; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + incomplete_sdu_len = sdu_offset; + // sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + // sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as lost data */ + test_ial_sdu_verify(demux, incomplete_sdu_len, 0b10); +} + +static void +test_ial_bis_unf_snc_bi_02_c_round_8b(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + uint16_t incomplete_sdu_len; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + incomplete_sdu_len = sdu_offset; + // sdu_offset += test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + // sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as data with possible errors */ + test_ial_sdu_verify(demux, incomplete_sdu_len, 0b01); +} + +static void +test_ial_bis_unf_snc_bi_02_c_round_8c(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + uint16_t incomplete_sdu_len; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + incomplete_sdu_len = sdu_offset; + // sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + // sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as data with possible errors */ + test_ial_sdu_verify(demux, incomplete_sdu_len, 0b01); +} + +static void +test_ial_bis_unf_snc_bi_02_c_round_8d(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + uint16_t incomplete_sdu_len; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + // sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + // sdu_len - sdu_offset, 0b00); + incomplete_sdu_len = sdu_offset; + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as data with possible errors */ + test_ial_sdu_verify(demux, incomplete_sdu_len, 0b01); +} + +/** + * Send 2 PDUs with LLID=0b01 and data, then 1 PDU with LLID=0b00 and data; + * one of the four PDUs is omitted to simulate losing one PDU + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_8(struct ble_ll_isoal_demux *demux) +{ + test_ial_bis_unf_snc_bi_02_c_round_8a(demux); + test_ial_bis_unf_snc_bi_02_c_round_8b(demux); + test_ial_bis_unf_snc_bi_02_c_round_8c(demux); + test_ial_bis_unf_snc_bi_02_c_round_8d(demux); +} + +/** + * 1 PDU with LLID=0b00 and data, then 3 PDUs with LLID=0b01, at least one of which has data + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_9(struct ble_ll_isoal_demux *demux) +{ + uint16_t sdu_len, sdu_offset; + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], 0, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += + test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], 0, 0b01); + + ble_ll_isoal_demux_event_done(demux); + + /* Expected to be reported as data with possible errors */ + test_ial_sdu_verify(demux, sdu_offset, 0b01); +} +/** + * No PDUs + */ +static void +test_ial_bis_unf_snc_bi_02_c_round_10(struct ble_ll_isoal_demux *demux) +{ + ble_ll_isoal_demux_event_start(demux, 0); + ble_ll_isoal_demux_event_done(demux); + + /* Shall be reported as lost data */ + test_ial_sdu_verify(demux, 0, 0b10); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bi_02_c) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + const uint16_t ISO_Interval = 10000; + const uint16_t SDU_Interval = 10000; + const uint8_t Max_PDU = 32; + const uint8_t NSE = 4; + const uint8_t BN = 4; + + (void)NSE; + + test_ll_isoal_setup(&fixture, TSPX_max_sdu_length, Max_PDU, ISO_Interval, + SDU_Interval, BN, 0, 0); + + test_ial_bis_unf_snc_bi_02_c_round_1(demux); + test_ial_bis_unf_snc_bi_02_c_round_2(demux); + test_ial_bis_unf_snc_bi_02_c_round_3(demux); + test_ial_bis_unf_snc_bi_02_c_round_4(demux); + test_ial_bis_unf_snc_bi_02_c_round_5(demux); + test_ial_bis_unf_snc_bi_02_c_round_6(demux); + test_ial_bis_unf_snc_bi_02_c_round_7(demux); + test_ial_bis_unf_snc_bi_02_c_round_8(demux); + test_ial_bis_unf_snc_bi_02_c_round_9(demux); + test_ial_bis_unf_snc_bi_02_c_round_10(demux); + + test_ll_isoal_teardown(&fixture); +} + +TEST_CASE_SELF(test_ial_bis_unf_snc_bi_05_c) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + const uint16_t ISO_Interval = 10000; + const uint16_t SDU_Interval = 10000; + const uint8_t Max_PDU = 32; + const uint8_t NSE = 4; + const uint8_t BN = 4; + uint16_t sdu_len, sdu_offset; + + (void)NSE; + + test_ll_isoal_setup(&fixture, TSPX_max_sdu_length, Max_PDU, ISO_Interval, + SDU_Interval, BN, 0, 0); + + sdu_len = TSPX_max_sdu_length; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, 0); + + /* 1. The Lower Tester sends 2 unframed Start/Continuation ISO Data PDUs to + * the IUT with the LLID=0b01 + */ + sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + + /* 2. The Lower Tester sends the IUT 1 unframed Start/Continuation ISO Data + * PDU with an invalid CRC. + */ + /* NOP */ + + /* 3. The Lower Tester sends the last unframed ISO Data PDU to the IUT with + * the LLID=0b00 and with the remaining Payload Data. + */ + sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b00); + + ble_ll_isoal_demux_event_done(demux); + + /* Alternative 4A (The IUT reports the SDU as 0b01 “data with possible errors”) */ + test_ial_sdu_verify(demux, 0, 0b01); + + ble_ll_isoal_demux_event_start(demux, 0); + + sdu_offset = 0; + + /* 5. The Lower Tester sends unframed ISO Data PDUs to the IUT with all LLID = 0b01. */ + sdu_offset += test_ial_lt_unf_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 2, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + sdu_offset += test_ial_lt_unf_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0b01); + + ble_ll_isoal_demux_event_done(demux); + + /* Alternative 6A (The IUT reports the SDU as 0b01 “data with possible errors”) */ + test_ial_sdu_verify(demux, 0, 0b01); + + test_ll_isoal_teardown(&fixture); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bi_01_c) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + const uint16_t ISO_Interval = 10000; + const uint16_t SDU_Interval = 10000; + const uint8_t Max_SDU = 108; + const uint8_t Max_PDU = 32; + const uint8_t NSE = 4; + const uint8_t BN = 4; + uint16_t sdu_len, sdu_offset; + + (void)NSE; + + test_ll_isoal_setup(&fixture, Max_SDU, Max_PDU, ISO_Interval, SDU_Interval, + BN, 1, 0); + + sdu_len = Max_SDU; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, ISO_Interval); + + /* 1. The Lower Tester sends 2 framed Start/Continuation ISO Data PDUs to the IUT */ + sdu_offset += test_ial_lt_fra_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 100, true, false); + sdu_offset += test_ial_lt_fra_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, + 0 /* ignored */, false, false); + + /* 2. The Lower Tester sends 1 framed Start/Continuation ISO Data PDU to the IUT with the Length field in the Segmentation Header set to 255. */ + do { + const void *data = &g_test_sdu_data[sdu_offset]; + struct os_mbuf *om; + uint8_t *payload_len; + uint8_t *seg_len; + uint16_t dlen; + + dlen = sdu_len - sdu_offset; + + om = test_ial_lt_pdu_get(0b10, &payload_len); + test_ial_lt_pdu_seghdr_put(om, payload_len, false, false, 0, &seg_len); + + if (dlen + *payload_len > demux->config.max_pdu) { + dlen = demux->config.max_pdu - *payload_len; + } + + dlen = test_ial_lt_pdu_data_put(om, payload_len, data, dlen); + *seg_len = 255; + + test_ial_lt_pdu_send(om, 2, payload_len, demux); + } while (false); + + /* 3. The Lower Tester sends the last framed ISO Data PDU to the IUT with the remaining Payload Data. */ + sdu_offset += test_ial_lt_fra_pdu_send(demux, 3, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0, false, true); + + ble_ll_isoal_demux_event_done(demux); + + /* Alternative 4A (The IUT reports the SDU as 0b01 “data with possible errors”) */ + test_ial_sdu_verify(demux, 0, 0b01); + + test_ll_isoal_teardown(&fixture); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bi_02_c) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + const uint16_t ISO_Interval = 10000; + const uint16_t SDU_Interval = 10000; + const uint8_t Max_SDU = 32; + const uint8_t Max_PDU = 45; + const uint8_t NSE = 1; + const uint8_t BN = 1; + uint16_t sdu_len, sdu_offset; + + (void)NSE; + + test_ll_isoal_setup(&fixture, Max_SDU, Max_PDU, ISO_Interval, SDU_Interval, + BN, 1, 0); + + sdu_len = Max_SDU; + sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, ISO_Interval); + + /* 1. The Lower Tester sends 1 framed complete ISO Data PDU to the IUT with + * the Length field of the Segmentation Header set to 255. */ + do { + const void *data = &g_test_sdu_data[sdu_offset]; + struct os_mbuf *om; + uint8_t *payload_len; + uint8_t *seg_len; + uint16_t dlen; + + dlen = sdu_len - sdu_offset; + + om = test_ial_lt_pdu_get(0b10, &payload_len); + test_ial_lt_pdu_seghdr_put(om, payload_len, true, true, 100, &seg_len); + + if (dlen + *payload_len > demux->config.max_pdu) { + dlen = demux->config.max_pdu - *payload_len; + } + + dlen = test_ial_lt_pdu_data_put(om, payload_len, data, dlen); + *seg_len = 255; + + test_ial_lt_pdu_send(om, 0, payload_len, demux); + } while (false); + + ble_ll_isoal_demux_event_done(demux); + + /* 2A.1 The IUT sends an HCI ISO Data packet with data to the Upper Tester + * with the Packet_Status_Flag set to 0b01 “data with possible errors”. + */ + test_ial_sdu_verify(demux, 0, 0b01); + + test_ll_isoal_teardown(&fixture); +} + +/** @brief IAL/BIS/FRA/SNC/BI-03-C Step 1A + * + * The Lower Tester sends two ISO Data PDUs to the IUT with the LLID = 0b10 in the same + * isochronous interval. The IUT sends the Upper Tester an ISO Data packet with + * Packet_Status_Flag = 0b00 and PB_Flag = 0b10 and containing all the data. + * + * @param mux ISOAL multiplexer + */ +static void +test_ial_bis_fra_snc_bi_03_c_step_1a(struct ble_ll_isoal_demux *demux, uint32_t timestamp) +{ + uint16_t sdu_len = demux->config.max_sdu; + uint16_t sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, timestamp); + + /* 1. The Lower Tester sends 2 framed Start/Continuation ISO Data PDUs to the IUT */ + sdu_offset += test_ial_lt_fra_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 100, true, false); + sdu_offset += test_ial_lt_fra_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 0, false, true); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall report data with status set to 0b00 (data valid) */ + test_ial_sdu_verify(demux, sdu_offset, 0b00); +} + +/** @brief IAL/BIS/FRA/SNC/BI-03-C Step 1B + * + * The Lower Tester sends an ISO Data PDU with the LLID = 0b10 in the first sub-event of an + * ISO interval and nothing in the second sub-event. The IUT sends the Upper Tester an ISO + * Data packet either with Packet_Status_Flag = 0b01 and containing the data, or with + * Packet_Status_Flag = 0b10 and ISO_SDU_Length = 0 and with no data. + * + * @param mux ISOAL multiplexer + */ +static void +test_ial_bis_fra_snc_bi_03_c_step_1b(struct ble_ll_isoal_demux *demux, uint32_t timestamp) +{ + uint16_t sdu_len = demux->config.max_sdu; + uint16_t sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, timestamp); + + /* 1. The Lower Tester sends 2 framed Start/Continuation ISO Data PDUs to the IUT */ + sdu_offset += test_ial_lt_fra_pdu_send(demux, 0, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, 100, true, false); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall report data with status set to 0b01 (data invalid) */ + test_ial_sdu_verify(demux, sdu_offset, 0b01); +} + +/** @brief IAL/BIS/FRA/SNC/BI-03-C Step 1C + * + * The Lower Tester sends nothing in the first sub-event of an ISO interval and an ISO Data + * PDU with the LLID = 0b10 in the second sub-event. The IUT sends the Upper Tester an + * ISO Data packet either with Packet_Status_Flag = 0b01 and containing the data, or with + * Packet_Status_Flag = 0b10 and ISO_SDU_Length = 0 and with no data. + * + * @param mux ISOAL multiplexer + */ +static void +test_ial_bis_fra_snc_bi_03_c_step_1c(struct ble_ll_isoal_demux *demux, uint32_t timestamp) +{ + uint16_t sdu_len = demux->config.max_sdu; + uint16_t sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, timestamp); + + /* 1. The Lower Tester sends 2 framed Start/Continuation ISO Data PDUs to the IUT */ + sdu_offset += test_ial_lt_fra_pdu_send(demux, 1, &g_test_sdu_data[sdu_offset], + sdu_len - sdu_offset, + 0 /* ignored */, false, true); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall be reported as lost data */ + test_ial_sdu_verify(demux, sdu_offset, 0b10); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bi_03_c) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + const uint16_t ISO_Interval = 10000; + const uint16_t SDU_Interval = 10000; + const uint8_t Max_SDU = 16; + const uint8_t Max_PDU = 16; + const uint8_t NSE = 2; + const uint8_t BN = 2; + uint32_t timestamp; + + (void)NSE; + + test_ll_isoal_setup(&fixture, Max_SDU, Max_PDU, ISO_Interval, SDU_Interval, + BN, 1, 0); + + timestamp = ISO_Interval; + + test_ial_bis_fra_snc_bi_03_c_step_1a(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1b(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1c(demux, timestamp); + timestamp += ISO_Interval; + + test_ial_bis_fra_snc_bi_03_c_step_1b(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1c(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1a(demux, timestamp); + timestamp += ISO_Interval; + + test_ial_bis_fra_snc_bi_03_c_step_1c(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1a(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1b(demux, timestamp); + timestamp += ISO_Interval; + + test_ial_bis_fra_snc_bi_03_c_step_1c(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1b(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1a(demux, timestamp); + timestamp += ISO_Interval; + + test_ll_isoal_teardown(&fixture); +} + +/** @brief IAL/BIS/FRA/SNC/BI-03-C Step 1A + * + * The Lower Tester sends two ISO Data PDUs to the IUT with the LLID = 0b10 in the same + * isochronous interval. The IUT sends the Upper Tester an ISO Data packet with + * Packet_Status_Flag = 0b00 and PB_Flag = 0b10 and containing all the data. + * + * @param mux ISOAL multiplexer + */ +static void +test_ial_bis_fra_snc_bi_03_c_step_1a_harmony(struct ble_ll_isoal_demux *demux, + uint32_t timestamp) +{ + uint16_t sdu_len = demux->config.max_sdu; + uint16_t sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, timestamp); + + /* 1. The Lower Tester sends 2 framed Start/Continuation ISO Data PDUs to the IUT */ + sdu_offset += test_ial_lt_fra_pdu_send(demux, 0, &g_test_sdu_data, sdu_len, + 1.5 * demux->config.sdu_interval_us, + true, true); + sdu_offset += test_ial_lt_fra_pdu_send(demux, 1, &g_test_sdu_data, sdu_len, + 0.5 * demux->config.sdu_interval_us, + true, true); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall report data with status set to 0b00 (data valid) */ + test_ial_sdu_verify(demux, sdu_len, 0b00); + test_ial_sdu_verify(demux, sdu_len, 0b00); +} + +/** @brief IAL/BIS/FRA/SNC/BI-03-C Step 1B + * + * The Lower Tester sends an ISO Data PDU with the LLID = 0b10 in the first sub-event of an + * ISO interval and nothing in the second sub-event. The IUT sends the Upper Tester an ISO + * Data packet either with Packet_Status_Flag = 0b01 and containing the data, or with + * Packet_Status_Flag = 0b10 and ISO_SDU_Length = 0 and with no data. + * + * @param mux ISOAL multiplexer + */ +static void +test_ial_bis_fra_snc_bi_03_c_step_1b_harmony(struct ble_ll_isoal_demux *demux, + uint32_t timestamp) +{ + uint16_t sdu_len = demux->config.max_sdu; + uint16_t sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, timestamp); + + /* 1. The Lower Tester sends 2 framed Start/Continuation ISO Data PDUs to the IUT */ + sdu_offset += test_ial_lt_fra_pdu_send( + demux, 0, &g_test_sdu_data[sdu_offset], sdu_len - sdu_offset, + 1.5 * demux->config.sdu_interval_us, true, true); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall report data with status set to 0b01 (data invalid) */ + test_ial_sdu_verify(demux, sdu_offset, 0b00); + test_ial_sdu_verify(demux, sdu_offset, 0b10); +} + +/** @brief IAL/BIS/FRA/SNC/BI-03-C Step 1C + * + * The Lower Tester sends nothing in the first sub-event of an ISO interval and an ISO Data + * PDU with the LLID = 0b10 in the second sub-event. The IUT sends the Upper Tester an + * ISO Data packet either with Packet_Status_Flag = 0b01 and containing the data, or with + * Packet_Status_Flag = 0b10 and ISO_SDU_Length = 0 and with no data. + * + * @param mux ISOAL multiplexer + */ +static void +test_ial_bis_fra_snc_bi_03_c_step_1c_harmony(struct ble_ll_isoal_demux *demux, + uint32_t timestamp) +{ + uint16_t sdu_len = demux->config.max_sdu; + uint16_t sdu_offset = 0; + + ble_ll_isoal_demux_event_start(demux, timestamp); + + /* 1. The Lower Tester sends 2 framed Start/Continuation ISO Data PDUs to the IUT */ + sdu_offset += test_ial_lt_fra_pdu_send( + demux, 1, &g_test_sdu_data[sdu_offset], sdu_len - sdu_offset, + 0.5 * demux->config.sdu_interval_us, true, true); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall be reported as lost data */ + test_ial_sdu_verify(demux, sdu_offset, 0b10); + test_ial_sdu_verify(demux, sdu_offset, 0b00); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bi_03_c_harmony) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + const uint16_t ISO_Interval = 40000; + const uint16_t SDU_Interval = 20000; + const uint8_t Max_SDU = 11; + const uint8_t Max_PDU = 16; + const uint8_t NSE = 2; + const uint8_t BN = 2; + uint32_t timestamp; + + (void)NSE; + + test_ll_isoal_setup(&fixture, Max_SDU, Max_PDU, ISO_Interval, SDU_Interval, + BN, 1, 0); + + timestamp = ISO_Interval; + + test_ial_bis_fra_snc_bi_03_c_step_1a_harmony(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1b_harmony(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1c_harmony(demux, timestamp); + timestamp += ISO_Interval; + + test_ial_bis_fra_snc_bi_03_c_step_1b_harmony(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1c_harmony(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1a_harmony(demux, timestamp); + timestamp += ISO_Interval; + + test_ial_bis_fra_snc_bi_03_c_step_1c_harmony(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1a_harmony(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1b_harmony(demux, timestamp); + timestamp += ISO_Interval; + + test_ial_bis_fra_snc_bi_03_c_step_1c_harmony(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1b_harmony(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_03_c_step_1a_harmony(demux, timestamp); + timestamp += ISO_Interval; + + test_ll_isoal_teardown(&fixture); +} + +/** @brief IAL/BIS/FRA/SNC/BI-04-C Step 1A + * + * The Lower Tester sends an ISO Data PDU to the IUT with the LLID = 0b10. The + * IUT sends the Upper Tester an ISO Data packet with Packet_Status_Flag = 0b00 + * and PB_Flag = 0b10 and containing all the data. + * + * @param mux ISOAL multiplexer + */ +static void +test_ial_bis_fra_snc_bi_04_c_step_1a(struct ble_ll_isoal_demux *demux, uint32_t timestamp) +{ + ble_ll_isoal_demux_event_start(demux, timestamp); + + /* 1. The Lower Tester sends an ISO Data PDU to the IUT with the LLID = 0b10. */ + test_ial_lt_fra_pdu_send(demux, 0, &g_test_sdu_data[0], + demux->config.max_sdu, 500, true, true); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall report data with status set to 0b00 (data valid) */ + test_ial_sdu_verify(demux, demux->config.max_sdu, 0b00); +} + +/** @brief IAL/BIS/FRA/SNC/BI-04-C Step 1B + * + * The Lower Tester sends an ISO Null PDU. The IUT sends the Upper Tester an ISO Data + * packet with Packet_Status_Flag = 0b10 and ISO_SDU_Length = 0 and with no data. + * + * @param mux ISOAL multiplexer + */ +static void +test_ial_bis_fra_snc_bi_04_c_step_1b(struct ble_ll_isoal_demux *demux, uint32_t timestamp) +{ + ble_ll_isoal_demux_event_start(demux, timestamp); + + /* The Lower Tester sends an ISO Null PDU */ + test_ial_lt_fra_null_pdu_send(demux, 0); + + ble_ll_isoal_demux_event_done(demux); + + /* Shall report data with status set to 0b10 (data lost) */ + test_ial_sdu_verify(demux, 0, 0b10); +} + +TEST_CASE_SELF(test_ial_bis_fra_snc_bi_04_c) +{ + struct test_ll_isoal_fixture fixture; + struct ble_ll_isoal_demux *demux = &fixture.demux; + const uint16_t ISO_Interval = 10000; + const uint16_t SDU_Interval = 10000; + const uint8_t Max_SDU = 16; + const uint8_t Max_PDU = Max_SDU + 13; + const uint8_t NSE = 1; + const uint8_t BN = 1; + uint32_t timestamp; + + (void)NSE; + + timestamp = 33333; + + test_ll_isoal_setup(&fixture, Max_SDU, Max_PDU, ISO_Interval, SDU_Interval, + BN, 1, 0); + + test_ial_bis_fra_snc_bi_04_c_step_1a(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_04_c_step_1b(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_04_c_step_1a(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_04_c_step_1b(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_04_c_step_1b(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_04_c_step_1a(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_04_c_step_1b(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_04_c_step_1a(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_04_c_step_1a(demux, timestamp); + timestamp += ISO_Interval; + test_ial_bis_fra_snc_bi_04_c_step_1b(demux, timestamp); + timestamp += ISO_Interval; + + test_ll_isoal_teardown(&fixture); +} + +TEST_SUITE(ble_ll_isoal_test_suite) +{ + ble_ll_isoal_test_suite_init(); + + test_ble_ll_isoal_mux_init(); + test_ble_ll_isoal_mux_get_unframed_pdu(); + test_ble_ll_isoal_mux_sdu_not_in_event(); + + /* Broadcast Single SDU, BIS */ + test_ial_bis_unf_brd_bv_01_c(); + test_ial_bis_unf_brd_bv_02_c(); + test_ial_bis_fra_brd_bv_06_c(); + test_ial_bis_fra_brd_bv_08_c(); + test_ial_bis_fra_brd_bv_29_c(); + + /* Broadcast Large SDU, BIS */ + test_ial_bis_unf_brd_bv_09_c(); + test_ial_bis_unf_brd_bv_10_c(); + test_ial_bis_unf_brd_bv_11_c(); + test_ial_bis_fra_brd_bv_13_c(); + test_ial_bis_fra_brd_bv_15_c(); + + /* Broadcast Multiple, Small SDUs, BIS */ + test_ial_bis_fra_brd_bv_17_c(); + test_ial_bis_fra_brd_bv_18_c(); + test_ial_bis_fra_brd_bv_20_c(); + + /* Broadcast a Zero-Length SDU, BIS */ + test_ial_bis_unf_brd_bv_21_c(); + test_ial_bis_unf_brd_bv_22_c(); + test_ial_bis_unf_brd_bv_23_c(); + test_ial_bis_unf_brd_bv_24_c(); + test_ial_bis_fra_brd_bv_25_c(); + test_ial_bis_fra_brd_bv_26_c(); + test_ial_bis_fra_brd_bv_27_c(); + test_ial_bis_fra_brd_bv_28_c(); + test_ial_bis_fra_brd_bv_30_c(); + + /* Broadcasting Unframed Empty PDUs with LLID=0b01, BIS */ + test_ial_bis_unf_brd_bv_29_c(); test_ial_bis_unf_early_sdus(); test_ial_bis_fra_early_sdus(); - ble_ll_isoal_reset(); + /* Receive a Single SDU, BIS */ + test_ial_bis_unf_snc_bv_01_c(); + test_ial_bis_unf_snc_bv_02_c(); + test_ial_bis_unf_snc_bv_03_c(); + test_ial_bis_fra_snc_bv_06_c(); + test_ial_bis_fra_snc_bv_08_c(); + test_ial_bis_fra_snc_bv_29_c(); + + /* Receive Large SDU, BIS */ + test_ial_bis_unf_snc_bv_09_c(); + test_ial_bis_unf_snc_bv_10_c(); + test_ial_bis_fra_snc_bv_11_c(); + test_ial_bis_fra_snc_bv_13_c(); + test_ial_bis_fra_snc_bv_15_c(); + + /* Receive Multiple, Small SDUs, BIS */ + test_ial_bis_fra_snc_bv_17_c(); + test_ial_bis_fra_snc_bv_18_c(); + test_ial_bis_fra_snc_bv_20_c(); + + /* Receive a Zero-Length SDU, BIS */ + test_ial_bis_unf_snc_bv_21_c(); + test_ial_bis_unf_snc_bv_22_c(); + test_ial_bis_unf_snc_bv_23_c(); + test_ial_bis_unf_snc_bv_24_c(); + test_ial_bis_fra_snc_bv_25_c(); + test_ial_bis_fra_snc_bv_26_c(); + test_ial_bis_fra_snc_bv_27_c(); + test_ial_bis_fra_snc_bv_28_c(); + test_ial_bis_fra_snc_bv_30_c(); + + /* Receive an unsuccessful Large SDU, BIS */ + test_ial_bis_unf_snc_bi_02_c(); + + /* SDU Reporting, BIS, Unframed PDU */ + test_ial_bis_unf_snc_bi_05_c(); + + /* SDU Reporting, BIS, Framed PDU */ + test_ial_bis_fra_snc_bi_01_c(); + + /* SDU Reporting, BIS, BN = 1, NSE = 1, Framed PDU */ + test_ial_bis_fra_snc_bi_02_c(); + + /* Reporting an Unsuccessful Large SDU, Framed BIS */ + test_ial_bis_fra_snc_bi_03_c(); + test_ial_bis_fra_snc_bi_03_c_harmony(); + + /* Reporting a missing or damaged SDU, Framed BIS */ + test_ial_bis_fra_snc_bi_04_c(); } diff --git a/nimble/include/nimble/ble.h b/nimble/include/nimble/ble.h index 0370f966cd..d0d6942ae8 100644 --- a/nimble/include/nimble/ble.h +++ b/nimble/include/nimble/ble.h @@ -75,8 +75,8 @@ struct ble_encryption_block struct ble_mbuf_hdr_rxinfo { uint16_t flags; + uint16_t handle; uint8_t channel; - uint8_t handle; int8_t rssi; /* XXX: we could just use single phy_mode field */ int8_t phy; @@ -84,7 +84,7 @@ struct ble_mbuf_hdr_rxinfo #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) int8_t rpa_index; #endif -#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) || MYNEWT_VAL(BLE_LL_ISO) void *user_data; #endif }; @@ -96,26 +96,26 @@ struct ble_mbuf_hdr_rxinfo * set for the same PDU (e.g. one use by scanner, other one used by * connection) */ -#define BLE_MBUF_HDR_F_CONNECT_IND_TXD (0x4000) -#define BLE_MBUF_HDR_F_CONNECT_REQ_TXD (0x4000) -#define BLE_MBUF_HDR_F_CONNECT_RSP_RXD (0x0008) -#define BLE_MBUF_HDR_F_CONN_CREDIT (0x8000) -#define BLE_MBUF_HDR_F_IGNORED (0x8000) -#define BLE_MBUF_HDR_F_CONN_CREDIT_INT (0x4000) -#define BLE_MBUF_HDR_F_SCAN_REQ_TXD (0x4000) -#define BLE_MBUF_HDR_F_INITA_RESOLVED (0x2000) -#define BLE_MBUF_HDR_F_TARGETA_RESOLVED (0x2000) -#define BLE_MBUF_HDR_F_EXT_ADV_SEC (0x1000) -#define BLE_MBUF_HDR_F_EXT_ADV (0x0800) -#define BLE_MBUF_HDR_F_RESOLVED (0x0400) -#define BLE_MBUF_HDR_F_AUX_PTR_WAIT (0x0200) -#define BLE_MBUF_HDR_F_AUX_PTR_FAILED (0x0100) -#define BLE_MBUF_HDR_F_CRC_OK (0x0080) -#define BLE_MBUF_HDR_F_DEVMATCH (0x0040) -#define BLE_MBUF_HDR_F_MIC_FAILURE (0x0020) -#define BLE_MBUF_HDR_F_SCAN_RSP_TXD (0x0010) -#define BLE_MBUF_HDR_F_SCAN_RSP_RXD (0x0008) -#define BLE_MBUF_HDR_F_RXSTATE_MASK (0x0007) +#define BLE_MBUF_HDR_F_CONNECT_IND_TXD (0x4000 << 1) +#define BLE_MBUF_HDR_F_CONNECT_REQ_TXD (0x4000 << 1) +#define BLE_MBUF_HDR_F_CONNECT_RSP_RXD (0x0008 << 1) +#define BLE_MBUF_HDR_F_CONN_CREDIT (0x8000 << 1) +#define BLE_MBUF_HDR_F_IGNORED (0x8000 << 1) +#define BLE_MBUF_HDR_F_CONN_CREDIT_INT (0x4000 << 1) +#define BLE_MBUF_HDR_F_SCAN_REQ_TXD (0x4000 << 1) +#define BLE_MBUF_HDR_F_INITA_RESOLVED (0x2000 << 1) +#define BLE_MBUF_HDR_F_TARGETA_RESOLVED (0x2000 << 1) +#define BLE_MBUF_HDR_F_EXT_ADV_SEC (0x1000 << 1) +#define BLE_MBUF_HDR_F_EXT_ADV (0x0800 << 1) +#define BLE_MBUF_HDR_F_RESOLVED (0x0400 << 1) +#define BLE_MBUF_HDR_F_AUX_PTR_WAIT (0x0200 << 1) +#define BLE_MBUF_HDR_F_AUX_PTR_FAILED (0x0100 << 1) +#define BLE_MBUF_HDR_F_CRC_OK (0x0080 << 1) +#define BLE_MBUF_HDR_F_DEVMATCH (0x0040 << 1) +#define BLE_MBUF_HDR_F_MIC_FAILURE (0x0020 << 1) +#define BLE_MBUF_HDR_F_SCAN_RSP_TXD (0x0010 << 1) +#define BLE_MBUF_HDR_F_SCAN_RSP_RXD (0x0008 << 1) +#define BLE_MBUF_HDR_F_RXSTATE_MASK (0x000F) /* Transmit info. NOTE: no flags defined */ struct ble_mbuf_hdr_txinfo diff --git a/nimble/include/nimble/hci_common.h b/nimble/include/nimble/hci_common.h index 01c3dbc14b..b68859ad5a 100644 --- a/nimble/include/nimble/hci_common.h +++ b/nimble/include/nimble/hci_common.h @@ -2448,6 +2448,7 @@ struct hci_data_hdr #define BLE_HCI_ISO_PKT_STATUS_FLAG_MASK (0xC000) #define BLE_HCI_ISO_HANDLE(ch, pb, ts) ((ch) | ((pb) << 12) | ((ts) << 14)) +#define BLE_HCI_ISO_SDU_LENGTH_DEFINE(l, psf) ((l) | ((psf) << 14)) #define BLE_HCI_ISO_CONN_HANDLE(h) ((h) & BLE_HCI_ISO_CONN_HANDLE_MASK) #define BLE_HCI_ISO_PB_FLAG(h) (((h) & BLE_HCI_ISO_PB_FLAG_MASK) >> 12) @@ -2461,9 +2462,9 @@ struct hci_data_hdr #define BLE_HCI_ISO_PB_COMPLETE (2) #define BLE_HCI_ISO_PB_LAST (3) -#define BLE_HCI_ISO_PKT_STATUS_VALID 0x00 -#define BLE_HCI_ISO_PKT_STATUS_INVALID 0x01 -#define BLE_HCI_ISO_PKT_STATUS_LOST 0x10 +#define BLE_HCI_ISO_PKT_STATUS_VALID 0b00 +#define BLE_HCI_ISO_PKT_STATUS_INVALID 0b01 +#define BLE_HCI_ISO_PKT_STATUS_LOST 0b10 #define BLE_HCI_ISO_BIG_HANDLE_MIN 0x00 #define BLE_HCI_ISO_BIG_HANDLE_MAX 0xEF diff --git a/nimble/transport/ipc/icbmsg/src/icbmsg_ble_hci.c b/nimble/transport/ipc/icbmsg/src/icbmsg_ble_hci.c index d120260173..2d70a6bc36 100644 --- a/nimble/transport/ipc/icbmsg/src/icbmsg_ble_hci.c +++ b/nimble/transport/ipc/icbmsg/src/icbmsg_ble_hci.c @@ -78,13 +78,11 @@ icbmsg_ble_hci_acl_tx(struct os_mbuf *om) return icbmsg_ble_hci_send_mbuf(HCI_IPC_TYPE_ACL, om); } -#if !MYNEWT_VAL(BLE_CONTROLLER) static int icbmsg_ble_hci_iso_tx(struct os_mbuf *om) { return icbmsg_ble_hci_send_mbuf(HCI_IPC_TYPE_ISO, om); } -#endif static void ble_hci_trans_rx(const void *data, size_t len, void *user_data) @@ -147,6 +145,12 @@ ble_transport_to_hs_acl_impl(struct os_mbuf *om) return icbmsg_ble_hci_acl_tx(om); } +int +ble_transport_to_hs_iso_impl(struct os_mbuf *om) +{ + return icbmsg_ble_hci_iso_tx(om); +} + void ble_transport_hs_init(void) { diff --git a/nimble/transport/nrf5340/src/nrf5340_ble_hci.c b/nimble/transport/nrf5340/src/nrf5340_ble_hci.c index f73be05522..3a132e9263 100644 --- a/nimble/transport/nrf5340/src/nrf5340_ble_hci.c +++ b/nimble/transport/nrf5340/src/nrf5340_ble_hci.c @@ -78,7 +78,6 @@ nrf5340_ble_hci_acl_tx(struct os_mbuf *om) return (rc < 0) ? BLE_ERR_MEM_CAPACITY : 0; } -#if !MYNEWT_VAL(BLE_CONTROLLER) static int nrf5340_ble_hci_iso_tx(struct os_mbuf *om) { @@ -105,7 +104,6 @@ nrf5340_ble_hci_iso_tx(struct os_mbuf *om) return (rc < 0) ? BLE_ERR_MEM_CAPACITY : 0; } -#endif static void nrf5340_ble_hci_trans_rx(int channel, void *user_data) @@ -163,6 +161,12 @@ ble_transport_to_hs_acl_impl(struct os_mbuf *om) return nrf5340_ble_hci_acl_tx(om); } +int +ble_transport_to_hs_iso_impl(struct os_mbuf *om) +{ + return nrf5340_ble_hci_iso_tx(om); +} + void ble_transport_hs_init(void) { diff --git a/nimble/transport/uart/src/hci_uart.c b/nimble/transport/uart/src/hci_uart.c index af1f45ae92..3515f894c8 100644 --- a/nimble/transport/uart/src/hci_uart.c +++ b/nimble/transport/uart/src/hci_uart.c @@ -111,6 +111,7 @@ hci_uart_tx_char(void *arg) } break; case HCI_H4_ACL: + case HCI_H4_ISO: os_mbuf_copydata(tx->om, 0, 1, &ch); os_mbuf_adj(tx->om, 1); tx->len--; diff --git a/nimble/transport/usb/src/ble_hci_usb.c b/nimble/transport/usb/src/ble_hci_usb.c index 01c60b88d9..7b09929004 100644 --- a/nimble/transport/usb/src/ble_hci_usb.c +++ b/nimble/transport/usb/src/ble_hci_usb.c @@ -241,6 +241,14 @@ ble_hci_trans_ll_evt_tx(void *buf) return 0; } +int +ble_transport_to_hs_iso_impl(struct os_mbuf *om) +{ + /* TODO */ + os_mbuf_free_chain(om); + return 0; +} + int ble_transport_to_hs_acl_impl(struct os_mbuf *om) {