This is an automated email from the ASF dual-hosted git repository. andk pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
commit 03f4d1645adf1ad13041ec4b650ed207a45b2edd Author: Mariusz Skamra <[email protected]> AuthorDate: Tue Jan 7 11:30:09 2025 +0100 nimble/ll/isoal: Add initial framed PDU ISOAL support This adds framed PDU ISOAL support --- nimble/controller/include/controller/ble_ll_iso.h | 35 +- .../controller/include/controller/ble_ll_iso_big.h | 3 - .../controller/include/controller/ble_ll_isoal.h | 43 +- nimble/controller/src/ble_ll.c | 11 +- nimble/controller/src/ble_ll_hci.c | 2 +- nimble/controller/src/ble_ll_iso.c | 194 ++++- nimble/controller/src/ble_ll_iso_big.c | 103 +-- nimble/controller/src/ble_ll_isoal.c | 375 +++++--- nimble/controller/test/src/ble_ll_isoal.c | 966 +++++++++++++++++++++ nimble/controller/test/src/ble_ll_test.c | 2 + nimble/controller/test/syscfg.yml | 4 + nimble/include/nimble/ble.h | 2 + nimble/include/nimble/hci_common.h | 4 + 13 files changed, 1501 insertions(+), 243 deletions(-) diff --git a/nimble/controller/include/controller/ble_ll_iso.h b/nimble/controller/include/controller/ble_ll_iso.h index fb20a4f34..768dd9923 100644 --- a/nimble/controller/include/controller/ble_ll_iso.h +++ b/nimble/controller/include/controller/ble_ll_iso.h @@ -21,11 +21,28 @@ #define H_BLE_LL_ISO #include <stdint.h> +#include <controller/ble_ll_isoal.h> #ifdef __cplusplus extern "C" { #endif +struct ble_ll_iso_conn { + /* Connection handle */ + uint16_t handle; + + /* 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); @@ -47,7 +64,23 @@ 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_end_test(const uint8_t *cmdbuf, uint8_t len); -struct ble_ll_isoal_mux *ble_ll_iso_find_mux_by_handle(uint16_t conn_handle); +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, uint8_t *llid, void *dptr); + +void ble_ll_iso_conn_init(struct ble_ll_iso_conn *conn, uint16_t conn_handle, + uint8_t max_pdu, uint32_t iso_interval_us, + uint32_t sdu_interval_us, uint8_t bn, uint8_t pte, uint8_t framing); +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); #ifdef __cplusplus } diff --git a/nimble/controller/include/controller/ble_ll_iso_big.h b/nimble/controller/include/controller/ble_ll_iso_big.h index 47d33bd3b..8576fc549 100644 --- a/nimble/controller/include/controller/ble_ll_iso_big.h +++ b/nimble/controller/include/controller/ble_ll_iso_big.h @@ -33,9 +33,6 @@ int ble_ll_iso_big_biginfo_copy(struct ble_ll_iso_big *big, uint8_t *dptr, uint32_t base_ticks, uint8_t base_rem_us); int ble_ll_iso_big_biginfo_len(struct ble_ll_iso_big *big); -struct ble_ll_iso_bis *ble_ll_iso_big_find_bis_by_handle(uint16_t conn_handle); -struct ble_ll_isoal_mux *ble_ll_iso_big_find_mux_by_handle(uint16_t conn_handle); - void ble_ll_iso_big_chan_map_update(void); void ble_ll_iso_big_halt(void); diff --git a/nimble/controller/include/controller/ble_ll_isoal.h b/nimble/controller/include/controller/ble_ll_isoal.h index 3bb7075bc..a590a0f6e 100644 --- a/nimble/controller/include/controller/ble_ll_isoal.h +++ b/nimble/controller/include/controller/ble_ll_isoal.h @@ -20,14 +20,13 @@ #ifndef H_BLE_LL_ISOAL_ #define H_BLE_LL_ISOAL_ +#include <os/os_mbuf.h> #include <syscfg/syscfg.h> #ifdef __cplusplus extern "C" { #endif -#if MYNEWT_VAL(BLE_LL_ISO) - struct ble_ll_isoal_mux { #if MYNEWT_VAL(BLE_LL_ISOAL_MUX_PREFILL) uint8_t active; @@ -44,37 +43,53 @@ struct ble_ll_isoal_mux { /* Number of SDUs available for current event */ uint8_t sdu_in_event; + /* Burst Number */ + uint8_t bn; + STAILQ_HEAD(, os_mbuf_pkthdr) sdu_q; uint16_t sdu_q_len; - struct os_mbuf *frag; - uint32_t sdu_counter; uint32_t event_tx_timestamp; uint32_t last_tx_timestamp; uint16_t last_tx_packet_seq_num; + + /* The head SDU Segment is the Continuation of an SDU */ + uint8_t sc : 1; + uint8_t framed : 1; + uint8_t framing_mode : 1; }; -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); +#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_MUX_IS_FRAMED(framing) \ + ((framing) == BLE_HCI_ISO_FRAMING_FRAMED_SEGMENTABLE || \ + (framing) == BLE_HCI_ISO_FRAMING_FRAMED_UNSEGMENTED) + +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); 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_unframed_get(struct ble_ll_isoal_mux *mux, uint8_t idx, - uint8_t *llid, void *dptr); +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_enqueue(struct ble_ll_isoal_mux *mux, + struct os_mbuf *om); void ble_ll_isoal_init(void); void ble_ll_isoal_reset(void); -int ble_ll_isoal_data_in(struct os_mbuf *om); - -#endif /* BLE_LL_ISO */ #ifdef __cplusplus } diff --git a/nimble/controller/src/ble_ll.c b/nimble/controller/src/ble_ll.c index 238bfad1d..cc11a93b4 100644 --- a/nimble/controller/src/ble_ll.c +++ b/nimble/controller/src/ble_ll.c @@ -47,7 +47,7 @@ #include "controller/ble_ll_sync.h" #include "controller/ble_fem.h" #if MYNEWT_VAL(BLE_LL_ISO) -#include "controller/ble_ll_isoal.h" +#include "controller/ble_ll_iso.h" #endif #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) #include "controller/ble_ll_iso_big.h" @@ -1685,13 +1685,14 @@ ble_ll_reset(void) ble_fem_lna_init(); #endif -#if MYNEWT_VAL(BLE_LL_ISO) - ble_ll_isoal_reset(); -#endif #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) ble_ll_iso_big_reset(); #endif +#if MYNEWT_VAL(BLE_LL_ISO) + ble_ll_iso_reset(); +#endif + /* Re-initialize the PHY */ rc = ble_phy_init(); @@ -1968,7 +1969,7 @@ ble_ll_init(void) #endif #if MYNEWT_VAL(BLE_LL_ISO) - ble_ll_isoal_init(); + ble_ll_iso_init(); #endif #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) ble_ll_iso_big_init(); diff --git a/nimble/controller/src/ble_ll_hci.c b/nimble/controller/src/ble_ll_hci.c index e8250284e..e969963ae 100644 --- a/nimble/controller/src/ble_ll_hci.c +++ b/nimble/controller/src/ble_ll_hci.c @@ -1995,7 +1995,7 @@ int ble_ll_hci_iso_rx(struct os_mbuf *om) { #if MYNEWT_VAL(BLE_LL_ISO) - ble_ll_isoal_data_in(om); + ble_ll_iso_data_in(om); #else os_mbuf_free_chain(om); #endif diff --git a/nimble/controller/src/ble_ll_iso.c b/nimble/controller/src/ble_ll_iso.c index 46e25ee52..034996d5f 100644 --- a/nimble/controller/src/ble_ll_iso.c +++ b/nimble/controller/src/ble_ll_iso.c @@ -23,27 +23,26 @@ #include <controller/ble_ll.h> #include <controller/ble_ll_isoal.h> #include <controller/ble_ll_iso.h> -#include <controller/ble_ll_iso_big.h> +#include <controller/ble_ll_tmr.h> #if MYNEWT_VAL(BLE_LL_ISO) +STAILQ_HEAD(ble_ll_iso_conn_q, ble_ll_iso_conn); +struct ble_ll_iso_conn_q ll_iso_conn_q; + 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; - struct ble_ll_iso_bis *bis; + struct ble_ll_iso_conn *conn; uint16_t conn_handle; conn_handle = le16toh(cmd->conn_handle); - switch (BLE_LL_CONN_HANDLE_TYPE(conn_handle)) { - case BLE_LL_CONN_HANDLE_TYPE_BIS: - bis = ble_ll_iso_big_find_bis_by_handle(conn_handle); - if (bis) { - break; - } - default: + + conn = ble_ll_iso_conn_find_by_handle(conn_handle); + if (!conn) { return BLE_ERR_UNK_CONN_ID; } @@ -83,18 +82,18 @@ ble_ll_iso_read_tx_sync(const uint8_t *cmdbuf, uint8_t cmdlen, { 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_isoal_mux *mux; + struct ble_ll_iso_conn *iso_conn; uint16_t handle; handle = le16toh(cmd->conn_handle); - mux = ble_ll_iso_find_mux_by_handle(handle); - if (!mux) { + iso_conn = ble_ll_iso_conn_find_by_handle(handle); + if (!iso_conn) { return BLE_ERR_UNK_CONN_ID; } rsp->conn_handle = cmd->conn_handle; - rsp->packet_seq_num = htole16(mux->last_tx_packet_seq_num); - rsp->tx_timestamp = htole32(mux->last_tx_timestamp); + 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); @@ -102,15 +101,170 @@ ble_ll_iso_read_tx_sync(const uint8_t *cmdbuf, uint8_t cmdlen, return 0; } -struct ble_ll_isoal_mux * -ble_ll_iso_find_mux_by_handle(uint16_t conn_handle) +struct ble_ll_iso_conn * +ble_ll_iso_conn_find_by_handle(uint16_t conn_handle) +{ + struct ble_ll_iso_conn *conn; + + STAILQ_FOREACH(conn, &ll_iso_conn_q, iso_conn_q_next) { + if (conn_handle == conn->handle) { + return conn; + } + } + + return NULL; +} + +void +ble_ll_iso_init(void) +{ + 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) { - switch (BLE_LL_CONN_HANDLE_TYPE(conn_handle)) { - case BLE_LL_CONN_HANDLE_TYPE_BIS: - return ble_ll_iso_big_find_mux_by_handle(conn_handle); + struct ble_hci_iso *hci_iso; + struct ble_hci_iso_data *hci_iso_data; + struct ble_ll_iso_conn *conn; + struct ble_mbuf_hdr *blehdr; + uint16_t data_hdr_len; + uint16_t handle; + uint16_t conn_handle; + uint16_t length; + uint16_t pb_flag; + uint16_t ts_flag; + uint32_t timestamp = 0; + + hci_iso = (void *)om->om_data; + + handle = le16toh(hci_iso->handle); + conn_handle = BLE_HCI_ISO_CONN_HANDLE(handle); + pb_flag = BLE_HCI_ISO_PB_FLAG(handle); + ts_flag = BLE_HCI_ISO_TS_FLAG(handle); + length = BLE_HCI_ISO_LENGTH(le16toh(hci_iso->length)); + + conn = ble_ll_iso_conn_find_by_handle(conn_handle); + if (!conn) { + os_mbuf_free_chain(om); + return BLE_ERR_UNK_CONN_ID; + } + + 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.cpu_timestamp = ble_ll_tmr_get(); + + if (ts_flag) { + timestamp = get_le32(om->om_data + sizeof(*hci_iso)); + data_hdr_len += sizeof(uint32_t); + } + blehdr->txiso.hci_timestamp = timestamp; + + hci_iso_data = (void *)(om->om_data + sizeof(*hci_iso) + data_hdr_len); + data_hdr_len += sizeof(*hci_iso_data); + } + os_mbuf_adj(om, sizeof(*hci_iso) + data_hdr_len); + + if (OS_MBUF_PKTLEN(om) != length - data_hdr_len) { + os_mbuf_free_chain(om); + return BLE_ERR_MEM_CAPACITY; + } + + switch (pb_flag) { + case BLE_HCI_ISO_PB_FIRST: + BLE_LL_ASSERT(!conn->frag); + conn->frag = om; + om = NULL; + break; + case BLE_HCI_ISO_PB_CONTINUATION: + BLE_LL_ASSERT(conn->frag); + os_mbuf_concat(conn->frag, om); + om = NULL; + break; + case BLE_HCI_ISO_PB_COMPLETE: + BLE_LL_ASSERT(!conn->frag); + break; + case BLE_HCI_ISO_PB_LAST: + BLE_LL_ASSERT(conn->frag); + os_mbuf_concat(conn->frag, om); + om = conn->frag; + conn->frag = NULL; + break; default: - return NULL; + BLE_LL_ASSERT(0); + break; + } + + if (om) { + ble_ll_isoal_mux_sdu_enqueue(&conn->mux, om); } + + return 0; +} + +int +ble_ll_iso_pdu_get(struct ble_ll_iso_conn *conn, uint8_t idx, uint8_t *llid, void *dptr) +{ + return ble_ll_isoal_mux_pdu_get(&conn->mux, idx, llid, dptr); +} + +void +ble_ll_iso_conn_init(struct ble_ll_iso_conn *conn, uint16_t conn_handle, + uint8_t max_pdu, uint32_t iso_interval_us, + uint32_t sdu_interval_us, uint8_t bn, uint8_t pte, + uint8_t framing) +{ + os_sr_t sr; + + memset(conn, 0, sizeof(*conn)); + + conn->handle = conn_handle; + ble_ll_isoal_mux_init(&conn->mux, max_pdu, iso_interval_us, sdu_interval_us, + bn, pte, BLE_LL_ISOAL_MUX_IS_FRAMED(framing), + 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) +{ + 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); + + ble_ll_isoal_mux_free(&conn->mux); +} + +int +ble_ll_iso_conn_event_start(struct ble_ll_iso_conn *conn, uint32_t timestamp) +{ + ble_ll_isoal_mux_event_start(&conn->mux, timestamp); + + return 0; +} + +int +ble_ll_iso_conn_event_done(struct ble_ll_iso_conn *conn) +{ + conn->num_completed_pkt += ble_ll_isoal_mux_event_done(&conn->mux); + + return conn->num_completed_pkt; } #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 8976c5e26..2a5e60de3 100644 --- a/nimble/controller/src/ble_ll_iso_big.c +++ b/nimble/controller/src/ble_ll_iso_big.c @@ -27,7 +27,7 @@ #include <controller/ble_ll_adv.h> #include <controller/ble_ll_crypto.h> #include <controller/ble_ll_hci.h> -#include <controller/ble_ll_isoal.h> +#include <controller/ble_ll_iso.h> #include <controller/ble_ll_iso_big.h> #include <controller/ble_ll_pdu.h> #include <controller/ble_ll_sched.h> @@ -53,7 +53,6 @@ struct ble_ll_iso_big; struct ble_ll_iso_bis { struct ble_ll_iso_big *big; uint8_t num; - uint16_t conn_handle; uint32_t aa; uint32_t crc_init; @@ -69,8 +68,7 @@ struct ble_ll_iso_bis { uint8_t g; } tx; - struct ble_ll_isoal_mux mux; - uint16_t num_completed_pkt; + struct ble_ll_iso_conn conn; STAILQ_ENTRY(ble_ll_iso_bis) bis_q_next; }; @@ -88,8 +86,8 @@ struct big_params { uint16_t max_sdu; uint8_t max_pdu; uint8_t phy; + uint8_t framing; uint8_t interleaved : 1; - uint8_t framed : 1; uint8_t encrypted : 1; uint8_t broadcast_code[16]; }; @@ -112,6 +110,7 @@ struct ble_ll_iso_big { 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]; @@ -203,43 +202,6 @@ big_sched_set(struct ble_ll_iso_big *big) } } -struct ble_ll_iso_bis * -ble_ll_iso_big_find_bis_by_handle(uint16_t conn_handle) -{ - struct ble_ll_iso_bis *bis; - uint8_t bis_idx; - - if (!BLE_LL_CONN_HANDLE_IS_BIS(conn_handle)) { - return NULL; - } - - bis_idx = BLE_LL_CONN_HANDLE_IDX(conn_handle); - - if (bis_idx >= BIS_POOL_SIZE) { - return NULL; - } - - bis = &bis_pool[bis_idx]; - if (!bis->big) { - return NULL; - } - - return bis; -} - -struct ble_ll_isoal_mux * -ble_ll_iso_big_find_mux_by_handle(uint16_t conn_handle) -{ - struct ble_ll_iso_bis *bis; - - bis = ble_ll_iso_big_find_bis_by_handle(conn_handle); - if (bis) { - return &bis->mux; - } - - return NULL; -} - static void ble_ll_iso_big_biginfo_chanmap_update(struct ble_ll_iso_big *big) { @@ -275,8 +237,8 @@ ble_ll_iso_big_biginfo_calc(struct ble_ll_iso_big *big, uint32_t seed_aa) put_le24(buf, (big->irc << 20) | (big->bis_spacing)); buf += 3; - /* max_pdu, rfu */ - put_le16(buf, big->max_pdu); + /* max_pdu, rfu, framing_mode */ + put_le16(buf, big->max_pdu | (big->framing_mode) << 15); buf += 2; /* seed_access_address */ @@ -296,7 +258,8 @@ ble_ll_iso_big_biginfo_calc(struct ble_ll_iso_big *big, uint32_t seed_aa) buf += 5; /* bis_payload_cnt, framing */ - memset(buf, 0x00, 5); + memset(buf, 0x01, 5); + buf[4] |= (big->framed << 7) & 0x80; } int @@ -357,7 +320,7 @@ ble_ll_iso_big_biginfo_copy(struct ble_ll_iso_big *big, uint8_t *dptr, *dptr++ = (counter >> 8) & 0xff; *dptr++ = (counter >> 16) & 0xff; *dptr++ = (counter >> 24) & 0xff; - *dptr++ = (counter >> 32) & 0xff; + *dptr++ = ((counter >> 32) & 0x7f) | ((big->framed << 7) & 0x80); if (big->encrypted) { memcpy(dptr, big->giv, 8); @@ -390,7 +353,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_isoal_mux_free(&bis->mux); + ble_ll_iso_conn_free(&bis->conn); bis->big = NULL; bis_pool_free++; } @@ -503,15 +466,12 @@ 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_isoal_mux_event_done(&bis->mux); + num_completed_pkt = ble_ll_iso_conn_event_done(&bis->conn); if (hci_ev && num_completed_pkt) { idx = hci_ev_ncp->count++; - hci_ev_ncp->completed[idx].handle = htole16(bis->conn_handle); - hci_ev_ncp->completed[idx].packets = htole16(num_completed_pkt + - bis->num_completed_pkt); - bis->num_completed_pkt = 0; - } else { - bis->num_completed_pkt += num_completed_pkt; + hci_ev_ncp->completed[idx].handle = htole16(bis->conn.handle); + hci_ev_ncp->completed[idx].packets = htole16(num_completed_pkt); + bis->conn.num_completed_pkt = 0; } #if MYNEWT_VAL(BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS) @@ -523,7 +483,7 @@ ble_ll_iso_big_event_done(struct ble_ll_iso_big *big) */ exp = bis->mux.sdu_per_event - bis->mux.sdu_per_interval; idx = fb_hci_subev->count++; - fb_hci_subev->feedback[idx].handle = htole16(bis->conn_handle); + fb_hci_subev->feedback[idx].handle = htole16(bis->conn.handle); fb_hci_subev->feedback[idx].sdu_per_interval = bis->mux.sdu_per_interval; fb_hci_subev->feedback[idx].diff = (int8_t)(bis->mux.sdu_q_len - exp); } @@ -723,7 +683,7 @@ ble_ll_iso_big_subevent_pdu_cb(uint8_t *dptr, void *arg, uint8_t *hdr_byte) } #if 1 - pdu_len = ble_ll_isoal_mux_unframed_get(&bis->mux, idx, &llid, dptr); + pdu_len = ble_ll_iso_pdu_get(&bis->conn, idx, &llid, dptr); #else llid = 0; pdu_len = big->max_pdu; @@ -860,12 +820,10 @@ ble_ll_iso_big_event_sched_cb(struct ble_ll_sched_item *sch) ble_ll_tx_power_set(g_ble_ll_tx_power); - BLE_LL_ASSERT(!big->framed); - /* XXX calculate this in advance at the end of previous event? */ big->tx.subevents_rem = big->num_bis * big->nse; STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { - ble_ll_isoal_mux_event_start(&bis->mux, (uint64_t)big->event_start * + ble_ll_iso_conn_event_start(&bis->conn, (uint64_t)big->event_start * 1000000 / 32768 + big->event_start_us); @@ -969,6 +927,7 @@ ble_ll_iso_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, struct ble_ll_iso_bis *bis; struct ble_ll_adv_sm *advsm; uint32_t seed_aa; + uint16_t conn_handle; uint8_t pte; uint8_t gc; uint8_t idx; @@ -1046,10 +1005,11 @@ 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); - BLE_LL_ASSERT(!big->framed); + conn_handle = BLE_LL_CONN_HANDLE(BLE_LL_CONN_HANDLE_TYPE_BIS, idx); - ble_ll_isoal_mux_init(&bis->mux, bp->max_pdu, bp->iso_interval * 1250, - bp->sdu_interval, bp->bn, pte); + ble_ll_iso_conn_init(&bis->conn, conn_handle, bp->max_pdu, + bp->iso_interval * 1250, bp->sdu_interval, + bp->bn, pte, bp->framing); } bis_pool_free -= num_bis; @@ -1059,7 +1019,8 @@ ble_ll_iso_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis, big->irc = bp->irc; big->nse = bp->nse; big->interleaved = bp->interleaved; - big->framed = bp->framed; + 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; @@ -1288,7 +1249,7 @@ ble_ll_iso_big_hci_evt_complete(void) idx = 0; STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) { - evt->conn_handle[idx] = htole16(bis->conn_handle); + evt->conn_handle[idx] = htole16(bis->conn.handle); idx++; } @@ -1314,7 +1275,7 @@ ble_ll_iso_big_hci_create(const uint8_t *cmdbuf, uint8_t len) !IN_RANGE(le16toh(cmd->max_sdu), 0x0001, 0x0fff) || !IN_RANGE(le16toh(cmd->max_transport_latency), 0x0005, 0x0fa0) || !IN_RANGE(cmd->rtn, 0x00, 0x1e) || - (cmd->packing > 1) || (cmd->framing > 1) || (cmd->encryption) > 1) { + (cmd->packing > 1) || (cmd->framing > 2) || (cmd->encryption) > 1) { return BLE_ERR_INV_HCI_CMD_PARMS; } @@ -1341,7 +1302,7 @@ ble_ll_iso_big_hci_create(const uint8_t *cmdbuf, uint8_t len) bp.phy = BLE_PHY_CODED; } bp.interleaved = cmd->packing; - bp.framed = cmd->framing; + bp.framing = cmd->framing; bp.encrypted = cmd->encryption; memcpy(bp.broadcast_code, cmd->broadcast_code, 16); @@ -1424,7 +1385,7 @@ ble_ll_iso_big_hci_create_test(const uint8_t *cmdbuf, uint8_t len) bp.phy = BLE_PHY_CODED; } bp.interleaved = cmd->packing; - bp.framed = cmd->framing; + bp.framing = cmd->framing; bp.encrypted = cmd->encryption; memcpy(bp.broadcast_code, cmd->broadcast_code, 16); @@ -1434,7 +1395,7 @@ ble_ll_iso_big_hci_create_test(const uint8_t *cmdbuf, uint8_t len) iso_interval_us = bp.iso_interval * 1250; - if (!bp.framed) { + if (bp.framing == BLE_HCI_ISO_FRAMING_UNFRAMED) { /* sdu_interval shall be an integer multiple of iso_interval */ if (iso_interval_us % bp.sdu_interval) { return BLE_ERR_INV_HCI_CMD_PARMS; @@ -1491,7 +1452,6 @@ void ble_ll_iso_big_init(void) { struct ble_ll_iso_big *big; - struct ble_ll_iso_bis *bis; uint8_t idx; memset(big_pool, 0, sizeof(big_pool)); @@ -1509,11 +1469,6 @@ ble_ll_iso_big_init(void) ble_npl_event_init(&big->event_done, ble_ll_iso_big_event_done_ev, big); } - for (idx = 0; idx < BIS_POOL_SIZE; idx++) { - bis = &bis_pool[idx]; - bis->conn_handle = BLE_LL_CONN_HANDLE(BLE_LL_CONN_HANDLE_TYPE_BIS, idx); - } - big_pool_free = ARRAY_SIZE(big_pool); bis_pool_free = ARRAY_SIZE(bis_pool); } diff --git a/nimble/controller/src/ble_ll_isoal.c b/nimble/controller/src/ble_ll_isoal.c index 8e7bfa4d0..8c8516b6d 100644 --- a/nimble/controller/src/ble_ll_isoal.c +++ b/nimble/controller/src/ble_ll_isoal.c @@ -19,10 +19,8 @@ #include <stdint.h> #include <syscfg/syscfg.h> -#include <nimble/hci_common.h> #include <controller/ble_ll.h> #include <controller/ble_ll_isoal.h> -#include <controller/ble_ll_iso.h> #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) @@ -30,27 +28,32 @@ #if MYNEWT_VAL(BLE_LL_ISO) -STAILQ_HEAD(ble_ll_iso_tx_q, os_mbuf_pkthdr); - -static struct ble_npl_event ll_isoal_tx_pkt_in; -static struct ble_ll_iso_tx_q ll_isoal_tx_q; - 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) + uint8_t bn, uint8_t pte, bool framed, uint8_t framing_mode) { memset(mux, 0, sizeof(*mux)); 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->pdu_per_sdu = bn / mux->sdu_per_interval; + + if (framed) { + /* TODO */ + } else { + mux->pdu_per_sdu = bn / mux->sdu_per_interval; + } mux->sdu_per_event = (1 + pte) * mux->sdu_per_interval; + mux->bn = bn; + STAILQ_INIT(&mux->sdu_q); mux->sdu_q_len = 0; + + mux->framed = framed; + mux->framing_mode = framing_mode; } void @@ -77,54 +80,20 @@ ble_ll_isoal_mux_free(struct ble_ll_isoal_mux *mux) STAILQ_INIT(&mux->sdu_q); } -static void -ble_ll_isoal_mux_tx_pkt_in(struct ble_ll_isoal_mux *mux, struct os_mbuf *om, - uint8_t pb, uint32_t timestamp) +void +ble_ll_isoal_mux_sdu_enqueue(struct ble_ll_isoal_mux *mux, struct os_mbuf *om) { struct os_mbuf_pkthdr *pkthdr; - struct ble_mbuf_hdr *blehdr; os_sr_t sr; BLE_LL_ASSERT(mux); - switch (pb) { - case BLE_HCI_ISO_PB_FIRST: - BLE_LL_ASSERT(!mux->frag); - mux->frag = om; - om = NULL; - break; - case BLE_HCI_ISO_PB_CONTINUATION: - BLE_LL_ASSERT(mux->frag); - os_mbuf_concat(mux->frag, om); - om = NULL; - break; - case BLE_HCI_ISO_PB_COMPLETE: - BLE_LL_ASSERT(!mux->frag); - break; - case BLE_HCI_ISO_PB_LAST: - BLE_LL_ASSERT(mux->frag); - os_mbuf_concat(mux->frag, om); - om = mux->frag; - mux->frag = NULL; - break; - default: - BLE_LL_ASSERT(0); - break; - } - - if (!om) { - return; - } - - blehdr = BLE_MBUF_HDR_PTR(om); - blehdr->txiso.packet_seq_num = ++mux->sdu_counter; - OS_ENTER_CRITICAL(sr); pkthdr = OS_MBUF_PKTHDR(om); STAILQ_INSERT_TAIL(&mux->sdu_q, pkthdr, omp_next); mux->sdu_q_len++; #if MYNEWT_VAL(BLE_LL_ISOAL_MUX_PREFILL) - if (mux->sdu_q_len == mux->sdu_per_event) { + if (mux->sdu_q_len >= mux->sdu_per_event) { mux->active = 1; } #endif @@ -138,26 +107,32 @@ ble_ll_isoal_mux_event_start(struct ble_ll_isoal_mux *mux, uint32_t timestamp) /* If prefill is enabled, we always expect to have required number of SDUs * in queue, otherwise we disable mux until enough SDUs are queued again. */ - mux->sdu_in_event = mux->sdu_per_event; - if (mux->sdu_in_event > mux->sdu_q_len) { + if (mux->sdu_per_event > mux->sdu_q_len) { mux->active = 0; } - if (!mux->active) { + if (mux->active && mux->framed) { + mux->sdu_in_event = mux->sdu_q_len; + } else if (mux->active) { + mux->sdu_in_event = mux->sdu_per_event; + } else { mux->sdu_in_event = 0; } #else - mux->sdu_in_event = min(mux->sdu_q_len, mux->sdu_per_event); + if (mux->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; } -int -ble_ll_isoal_mux_event_done(struct ble_ll_isoal_mux *mux) +static int +ble_ll_isoal_mux_unframed_event_done(struct ble_ll_isoal_mux *mux) { struct os_mbuf_pkthdr *pkthdr; - struct ble_mbuf_hdr *blehdr; struct os_mbuf *om; struct os_mbuf *om_next; uint8_t num_sdu; @@ -166,14 +141,6 @@ ble_ll_isoal_mux_event_done(struct ble_ll_isoal_mux *mux) num_sdu = min(mux->sdu_in_event, mux->sdu_per_interval); - 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 MYNEWT_VAL(BLE_LL_ISO_HCI_DISCARD_THRESHOLD) /* Drop queued SDUs if number of queued SDUs exceeds defined threshold. * Threshold is defined as number of ISO events. If number of queued SDUs @@ -188,6 +155,7 @@ ble_ll_isoal_mux_event_done(struct ble_ll_isoal_mux *mux) } #endif + pkthdr = STAILQ_FIRST(&mux->sdu_q); while (pkthdr && num_sdu--) { OS_ENTER_CRITICAL(sr); STAILQ_REMOVE_HEAD(&mux->sdu_q, omp_next); @@ -211,7 +179,119 @@ ble_ll_isoal_mux_event_done(struct ble_ll_isoal_mux *mux) return pkt_freed; } +static int +ble_ll_isoal_mux_framed_event_done(struct ble_ll_isoal_mux *mux) +{ + struct os_mbuf_pkthdr *pkthdr; + struct os_mbuf *om; + struct os_mbuf *om_next; + uint8_t num_sdu; + uint8_t num_pdu; + uint8_t pdu_offset = 0; + uint8_t frag_len = 0; + uint8_t rem_len = 0; + uint8_t hdr_len = 0; + int pkt_freed = 0; + bool sc = mux->sc; + os_sr_t sr; + + num_sdu = mux->sdu_in_event; + if (num_sdu == 0) { + return 0; + } + + num_pdu = mux->bn; + +#if MYNEWT_VAL(BLE_LL_ISO_HCI_DISCARD_THRESHOLD) + /* Drop queued SDUs if number of queued SDUs exceeds defined threshold. + * Threshold is defined as number of ISO events. If number of queued SDUs + * exceeds number of SDUs required for single event (i.e. including pt) + * and number of subsequent ISO events defined by threshold value, we'll + * drop any excessive SDUs and notify host as if they were sent. + */ + 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; + } +#endif + + /* Drop num_pdu PDUs */ + pkthdr = STAILQ_FIRST(&mux->sdu_q); + while (pkthdr && num_sdu--) { + om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + + while (om && num_pdu > 0) { + rem_len = om->om_len; + hdr_len = sc ? 2 /* Segmentation Header */ + : 5 /* Segmentation Header + TimeOffset */; + + if (mux->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); + + pdu_offset += hdr_len + frag_len; + + os_mbuf_adj(om, frag_len); + + if (frag_len == rem_len) { + om_next = SLIST_NEXT(om, om_next); + os_mbuf_free(om); + pkt_freed++; + om = om_next; + } else { + sc = 1; + } + } + + if (num_pdu == 0) { + break; + } + + OS_ENTER_CRITICAL(sr); + STAILQ_REMOVE_HEAD(&mux->sdu_q, omp_next); + BLE_LL_ASSERT(mux->sdu_q_len > 0); + mux->sdu_q_len--; + OS_EXIT_CRITICAL(sr); + + sc = 0; + pkthdr = STAILQ_FIRST(&mux->sdu_q); + } + + mux->sdu_in_event = 0; + mux->sc = sc; + + 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) { @@ -258,96 +338,141 @@ ble_ll_isoal_mux_unframed_get(struct ble_ll_isoal_mux *mux, uint8_t idx, return pdu_len; } -static void -ble_ll_isoal_tx_pkt_in(struct ble_npl_event *ev) +static int +ble_ll_isoal_mux_framed_get(struct ble_ll_isoal_mux *mux, uint8_t idx, + uint8_t *llid, uint8_t *dptr) { - struct os_mbuf *om; + struct ble_mbuf_hdr *blehdr; struct os_mbuf_pkthdr *pkthdr; - struct ble_hci_iso *hci_iso; - struct ble_hci_iso_data *hci_iso_data; - struct ble_ll_isoal_mux *mux; - uint16_t data_hdr_len; - uint16_t handle; - uint16_t conn_handle; - uint16_t length; - uint16_t pb_flag; - uint16_t ts_flag; - uint32_t timestamp = 0; - os_sr_t sr; + struct os_mbuf *om; + uint32_t time_offset; + uint16_t seghdr; + uint16_t rem_len = 0; + uint16_t sdu_offset = 0; + uint8_t num_sdu; + uint8_t num_pdu; + uint8_t frag_len; + uint8_t pdu_offset = 0; + bool sc = mux->sc; + uint8_t hdr_len = 0; + + *llid = 0b10; - while (STAILQ_FIRST(&ll_isoal_tx_q)) { - pkthdr = STAILQ_FIRST(&ll_isoal_tx_q); + num_sdu = mux->sdu_in_event; + if (num_sdu == 0) { + return 0; + } + + num_pdu = idx; + + /* Skip the idx PDUs */ + pkthdr = STAILQ_FIRST(&mux->sdu_q); + while (pkthdr && num_sdu > 0 && num_pdu > 0) { om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); - OS_ENTER_CRITICAL(sr); - STAILQ_REMOVE_HEAD(&ll_isoal_tx_q, omp_next); - OS_EXIT_CRITICAL(sr); + rem_len = OS_MBUF_PKTLEN(om) - sdu_offset; + hdr_len = sc ? 2 /* Segmentation Header */ + : 5 /* Segmentation Header + TimeOffset */; - hci_iso = (void *)om->om_data; + if (mux->max_pdu <= hdr_len + pdu_offset) { + /* Advance to next PDU */ + pdu_offset = 0; + num_pdu--; + continue; + } - handle = le16toh(hci_iso->handle); - conn_handle = BLE_HCI_ISO_CONN_HANDLE(handle); - pb_flag = BLE_HCI_ISO_PB_FLAG(handle); - ts_flag = BLE_HCI_ISO_TS_FLAG(handle); - length = BLE_HCI_ISO_LENGTH(le16toh(hci_iso->length)); + frag_len = min(rem_len, mux->max_pdu - hdr_len - pdu_offset); - data_hdr_len = 0; - if ((pb_flag == BLE_HCI_ISO_PB_FIRST) || - (pb_flag == BLE_HCI_ISO_PB_COMPLETE)) { - if (ts_flag) { - timestamp = get_le32(om->om_data + sizeof(*hci_iso)); - data_hdr_len += sizeof(uint32_t); - } + pdu_offset += hdr_len + frag_len; + + if (frag_len == rem_len) { + /* Process next SDU */ + sdu_offset = 0; + num_sdu--; + pkthdr = STAILQ_NEXT(pkthdr, omp_next); + + sc = 0; + } else { + sdu_offset += frag_len; - hci_iso_data = (void *)(om->om_data + sizeof(*hci_iso) + data_hdr_len); - data_hdr_len += sizeof(*hci_iso_data); + sc = 1; } - os_mbuf_adj(om, sizeof(*hci_iso) + data_hdr_len); + } - if (OS_MBUF_PKTLEN(om) != length - data_hdr_len) { - os_mbuf_free_chain(om); - continue; + if (num_pdu > 0) { + return 0; + } + + BLE_LL_ASSERT(pdu_offset == 0); + + while (pkthdr && num_sdu > 0) { + om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + + rem_len = OS_MBUF_PKTLEN(om) - sdu_offset; + hdr_len = sc ? 2 /* Segmentation Header */ + : 5 /* Segmentation Header + TimeOffset */; + + if (mux->max_pdu <= hdr_len + pdu_offset) { + break; } - mux = ble_ll_iso_find_mux_by_handle(conn_handle); - if (!mux) { - os_mbuf_free_chain(om); - continue; + frag_len = min(rem_len, mux->max_pdu - hdr_len - pdu_offset); + + /* Segmentation Header */ + seghdr = BLE_LL_ISOAL_SEGHDR(sc, frag_len == rem_len, frag_len + hdr_len - 2); + put_le16(dptr + pdu_offset, seghdr); + pdu_offset += 2; + + /* Time Offset */ + if (hdr_len > 2) { + blehdr = BLE_MBUF_HDR_PTR(om); + + time_offset = mux->event_tx_timestamp - + blehdr->txiso.cpu_timestamp; + put_le24(dptr + pdu_offset, time_offset); + pdu_offset += 3; + } + + /* ISO Data Fragment */ + os_mbuf_copydata(om, sdu_offset, frag_len, dptr + pdu_offset); + pdu_offset += frag_len; + + if (frag_len == rem_len) { + /* Process next SDU */ + sdu_offset = 0; + num_sdu--; + pkthdr = STAILQ_NEXT(pkthdr, omp_next); + + sc = 0; + } else { + sdu_offset += frag_len; + + sc = 1; } + } + + return pdu_offset; +} - ble_ll_isoal_mux_tx_pkt_in(mux, om, pb_flag, timestamp); +int +ble_ll_isoal_mux_pdu_get(struct ble_ll_isoal_mux *mux, uint8_t idx, + uint8_t *llid, void *dptr) +{ + if (mux->framed) { + return ble_ll_isoal_mux_framed_get(mux, idx, llid, dptr); } + + return ble_ll_isoal_mux_unframed_get(mux, idx, llid, dptr); } void ble_ll_isoal_init(void) { - STAILQ_INIT(&ll_isoal_tx_q); - ble_npl_event_init(&ll_isoal_tx_pkt_in, ble_ll_isoal_tx_pkt_in, NULL); } void ble_ll_isoal_reset(void) { - STAILQ_INIT(&ll_isoal_tx_q); - ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &ll_isoal_tx_pkt_in); -} - -int -ble_ll_isoal_data_in(struct os_mbuf *om) -{ - struct os_mbuf_pkthdr *hdr; - os_sr_t sr; - - hdr = OS_MBUF_PKTHDR(om); - - OS_ENTER_CRITICAL(sr); - STAILQ_INSERT_TAIL(&ll_isoal_tx_q, hdr, omp_next); - OS_EXIT_CRITICAL(sr); - - ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &ll_isoal_tx_pkt_in); - - return 0; } #endif /* BLE_LL_ISO */ diff --git a/nimble/controller/test/src/ble_ll_isoal.c b/nimble/controller/test/src/ble_ll_isoal.c new file mode 100644 index 000000000..c10006c14 --- /dev/null +++ b/nimble/controller/test/src/ble_ll_isoal.c @@ -0,0 +1,966 @@ +/* + * 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 <stdint.h> +#include <controller/ble_ll_isoal.h> +#include <os/os_mbuf.h> +#include <nimble/ble.h> +#include <nimble/hci_common.h> +#include <testutil/testutil.h> + +#define TSPX_max_sdu_length (503) +#define HCI_iso_sdu_max (MYNEWT_VAL(BLE_TRANSPORT_ISO_SIZE) - 4) + +#define MBUF_TEST_POOL_BUF_SIZE (TSPX_max_sdu_length + BLE_MBUF_MEMBLOCK_OVERHEAD) +#define MBUF_TEST_POOL_BUF_COUNT (10) + +os_membuf_t os_mbuf_membuf[OS_MEMPOOL_SIZE(MBUF_TEST_POOL_BUF_SIZE, MBUF_TEST_POOL_BUF_COUNT)]; + +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]; + +void +os_mbuf_test_setup(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"); + 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); + 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; + } + + 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); +} + +TEST_CASE_SELF(test_ble_ll_isoal_mux_init) { + struct ble_ll_isoal_mux 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_ASSERT(mux.pdu_per_sdu == (bn * sdu_interval_us) / iso_interval_us); + + ble_ll_isoal_mux_free(&mux); +} + +TEST_CASE_SELF(ble_ll_isoal_mux_pdu_get_unframed_1_sdu_2_pdu) { + struct ble_ll_isoal_mux mux; + struct os_mbuf *sdu_1, *sdu_2; + const uint32_t iso_interval_us = 20000; + const uint32_t sdu_interval_us = 10000; + const bool Framed = 0; + const bool Framing_Mode = 0; + const uint8_t bn = 6; + const uint8_t max_pdu = 40; + const uint8_t sdu_len = 3 * max_pdu; + static uint8_t data[40]; + int num_completed_pkt; + int pdu_len; + 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); + + /* SDU #1 */ + sdu_1 = os_mbuf_get_pkthdr(&os_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); + TEST_ASSERT_FATAL(rc == 0); + ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_1); + + /* SDU #2 */ + sdu_2 = os_mbuf_get_pkthdr(&os_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); + TEST_ASSERT_FATAL(rc == 0); + ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_2); + + ble_ll_isoal_mux_event_start(&mux, 90990); + + /* PDU #1 */ + 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); + 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); + 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); + 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); + 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); + 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); + TEST_ASSERT(num_completed_pkt > 0, "num_completed_pkt is incorrect %d", num_completed_pkt); + + ble_ll_isoal_mux_free(&mux); +} + +TEST_CASE_SELF(test_ble_ll_isoal_mux_get_unframed_pdu) { + struct ble_ll_isoal_mux mux; + struct os_mbuf *sdu_1, *sdu_2; + const uint32_t iso_interval_us = 20000; + const uint32_t sdu_interval_us = 10000; + const bool Framed = 0; + const bool Framing_Mode = 0; + const uint8_t bn = 6; + const uint8_t max_pdu = 40; + const uint8_t sdu_len = 3 * max_pdu; + static uint8_t data[40]; + int num_completed_pkt; + int pdu_len; + 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); + + /* SDU #1 */ + sdu_1 = os_mbuf_get_pkthdr(&os_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); + TEST_ASSERT_FATAL(rc == 0); + ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_1); + + /* SDU #2 */ + sdu_2 = os_mbuf_get_pkthdr(&os_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); + TEST_ASSERT_FATAL(rc == 0); + ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_2); + + ble_ll_isoal_mux_event_start(&mux, 90990); + + /* PDU #1 */ + 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); + 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); + 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); + 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); + 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); + 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); + TEST_ASSERT(num_completed_pkt > 0, "num_completed_pkt is incorrect %d", + num_completed_pkt); + + ble_ll_isoal_mux_free(&mux); +} + +TEST_CASE_SELF(test_ble_ll_isoal_mux_sdu_not_in_event) { + struct ble_ll_isoal_mux mux; + struct os_mbuf *sdu_1; + const uint32_t iso_interval_us = 10000; + const uint32_t sdu_interval_us = 10000; + const bool Framed = 1; + const bool Framing_Mode = 0; + const uint8_t bn = 2; + const uint8_t max_pdu = 40; + const uint8_t sdu_len = 40; + static uint8_t data[40]; + int num_completed_pkt; + int pdu_len; + 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); + + 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)); + TEST_ASSERT_FATAL(sdu_1 != NULL); + rc = os_mbuf_append(sdu_1, os_mbuf_test_data, sdu_len); + TEST_ASSERT_FATAL(rc == 0); + ble_ll_isoal_mux_sdu_enqueue(&mux, sdu_1); + + 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); + 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); + 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); + TEST_ASSERT(num_completed_pkt == 0, "num_completed_pkt is incorrect %d", + num_completed_pkt); + + ble_ll_isoal_mux_free(&mux); +} + +static int +test_sdu_enqueue(struct ble_ll_isoal_mux *mux, uint16_t sdu_len, + uint16_t packet_seq_num, uint32_t timestamp) +{ + struct ble_mbuf_hdr *blehdr; + struct os_mbuf *sdu, *frag; + uint16_t sdu_frag_len; + uint16_t offset = 0; + uint8_t num_pkt = 0; + int rc; + + 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)); + TEST_ASSERT_FATAL(sdu != NULL); + blehdr = BLE_MBUF_HDR_PTR(sdu); + blehdr->txiso.packet_seq_num = packet_seq_num; + blehdr->txiso.cpu_timestamp = timestamp; + + /* 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); + 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)); + 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); + TEST_ASSERT_FATAL(rc == 0); + + offset += sdu_frag_len; + num_pkt++; + + os_mbuf_concat(sdu, frag); + } + + ble_ll_isoal_mux_sdu_enqueue(mux, sdu); + + return num_pkt; +} + +static void +test_pdu_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]); + } +} + +struct test_ial_broadcast_single_sdu_bis_cfg { + uint8_t NSE; + uint8_t Framed; + uint8_t Framing_Mode; + uint8_t Max_PDU; + uint8_t LLID; + uint8_t BN; + uint32_t SDU_Interval; + 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; + int num_completed_pkt; + int pdu_len; + uint32_t timeoffset; + uint16_t seg_hdr; + const uint8_t Max_SDU = 32; + 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); + + /* Send Single SDU */ + test_sdu_enqueue(&mux, Max_SDU, 0, 20000); + + ble_ll_isoal_mux_event_start(&mux, 30500); + + /* PDU #1 */ + 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) { + TEST_ASSERT(pdu_len == 2 /* Header */ + 3 /* TimeOffset */ + Max_SDU, + "PDU length is incorrect %d", pdu_len); + seg_hdr = get_le16(&pdu[0]); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_SC(seg_hdr) == 0, "SC is incorrect %d", + BLE_LL_ISOAL_SEGHDR_SC(seg_hdr)); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr) == 1, "CMPLT is incorrect %d", + BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr)); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_LEN(seg_hdr) == 3 /* TimeOffset */ + Max_SDU, + "Length is incorrect %d", BLE_LL_ISOAL_SEGHDR_LEN(seg_hdr)); + timeoffset = get_le24(&pdu[2]); + TEST_ASSERT(timeoffset == 10500, "Time offset is incorrect %d", timeoffset); + + test_pdu_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); + } + + 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_CASE_SELF(test_ial_bis_unf_brd_bv_01_c) { + const struct test_ial_broadcast_single_sdu_bis_cfg cfg = { + .NSE = 2, + .Framed = 0, + .Framing_Mode = 0, + .Max_PDU = 40, + .LLID = 0b00, + .BN = 1, + .SDU_Interval = 10000, + .ISO_Interval = 10000, + }; + + test_ial_broadcast_single_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_unf_brd_bv_02_c) { + const struct test_ial_broadcast_single_sdu_bis_cfg cfg = { + .NSE = 4, + .Framed = 0, + .Framing_Mode = 0, + .Max_PDU = 40, + .LLID = 0b00, + .BN = 2, + .SDU_Interval = 5000, + .ISO_Interval = 10000, + }; + + test_ial_broadcast_single_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_06_c) { + const struct test_ial_broadcast_single_sdu_bis_cfg cfg = { + .NSE = 4, + .Framed = 1, + .Framing_Mode = 0, + .Max_PDU = 40, + .LLID = 0b10, + .BN = 2, + .SDU_Interval = 5000, + .ISO_Interval = 10000, + }; + + test_ial_broadcast_single_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_08_c) { + const struct test_ial_broadcast_single_sdu_bis_cfg cfg = { + .NSE = 2, + .Framed = 1, + .Framing_Mode = 0, + .Max_PDU = 40, + .LLID = 0b10, + .BN = 1, + .SDU_Interval = 10000, + .ISO_Interval = 10000, + }; + + test_ial_broadcast_single_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_29_c) { + const struct test_ial_broadcast_single_sdu_bis_cfg cfg = { + .NSE = 6, + .Framed = 1, + .Framing_Mode = 1, + .Max_PDU = 32 + 5, + .LLID = 0b10, + .BN = 3, + .SDU_Interval = 5000, + .ISO_Interval = 10000, + }; + + test_ial_broadcast_single_sdu_bis(&cfg); +} + +struct test_ial_broadcast_large_sdu_bis_cfg { + uint8_t NSE; + uint8_t Framed; + uint8_t Framing_Mode; + uint8_t BN; + uint32_t SDU_Interval; + uint32_t ISO_Interval; +}; +struct test_ial_broadcast_large_sdu_bis_round { + uint16_t sdu_len; + uint8_t sc_packets_num; +}; + +static void +test_ial_broadcast_large_sdu_bis(const struct test_ial_broadcast_large_sdu_bis_cfg *cfg) +{ + const struct test_ial_broadcast_large_sdu_bis_round rounds[] = { + {.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; */ + const uint8_t Max_PDU = 251; + int num_completed_pkt; + int num_expected_pkt; + int pdu_len; + uint8_t pdu[Max_PDU]; + uint32_t timestamp; + uint16_t seg_hdr; + uint16_t sdu_offset; + uint8_t llid = 0xff; + uint8_t sc_packets_num; + 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); + + for (size_t round = 0; round < ARRAY_SIZE(rounds); round++) { + sc_packets_num = 0; + sdu_offset = 0; + + timestamp = (round + 1) * cfg->SDU_Interval; + + num_expected_pkt = test_sdu_enqueue(&mux, rounds[round].sdu_len, round, timestamp); + + 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); + if (pdu_len == 0) { + TEST_ASSERT_FATAL(sdu_offset == rounds[round].sdu_len, + "Round #%d: idx %d sdu_offset %d", + round, idx, sdu_offset); + continue; + } + + /* The IUT sends the specified number of Start/Continuation + * packets specified in Table 4.29 of ISO Data PDUs to the + * Lower Tester with the LLID=0b01 for unframed payloads and + * LLID=0b10 for framed payloads, and Payload Data every 251 + * bytes offset in step 1. + */ + if (sc_packets_num < rounds[round].sc_packets_num) { + TEST_ASSERT_FATAL(pdu_len == 251, "Round #%d: idx #%d: Length is incorrect %d", + round, idx, pdu_len); + + if (cfg->Framed) { + TEST_ASSERT_FATAL(llid == 0b10, "Round #%d: LLID is incorrect %d", round, llid); + + seg_hdr = get_le16(&pdu[0]); + seg_len = BLE_LL_ISOAL_SEGHDR_LEN(seg_hdr); + if (idx == 0) { + TEST_ASSERT_FATAL(BLE_LL_ISOAL_SEGHDR_SC(seg_hdr) == 0, + "Round #%d: SC is incorrect %d", + round, BLE_LL_ISOAL_SEGHDR_SC(seg_hdr)); + + test_pdu_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); + 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); + sdu_offset += pdu_len; + } + + sc_packets_num++; + } else { + /* The IUT sends the last ISO Data PDU to the Lower Tester + * with the LLID=0b00 for unframed payloads and LLID=0b10 + * for framed payloads, with the remaining Payload Data. + */ + if (cfg->Framed) { + TEST_ASSERT_FATAL(pdu_len == rounds[round].sdu_len - sdu_offset + 2, + "Round #%d: idx %d: PDU length is incorrect %d != %d", + round, idx, pdu_len, rounds[round].sdu_len - sdu_offset + 2); + TEST_ASSERT_FATAL(llid == 0b10, "Round #%d: LLID is incorrect %d", + round, llid); + + seg_hdr = get_le16(&pdu[0]); + TEST_ASSERT_FATAL(BLE_LL_ISOAL_SEGHDR_SC(seg_hdr), + "Round #%d: SC is incorrect %d", + round, BLE_LL_ISOAL_SEGHDR_SC(seg_hdr)); + TEST_ASSERT_FATAL(BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr), + "Round #%d: CMPLT is incorrect %d", + 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); + sdu_offset += seg_len; + } else { + TEST_ASSERT_FATAL(pdu_len == rounds[round].sdu_len - sdu_offset, + "Round #%d: idx %d: PDU length is incorrect %d != %d", + 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); + sdu_offset += pdu_len; + } + } + } + + 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_CASE_SELF(test_ial_bis_fra_brd_bv_13_c) { + const struct test_ial_broadcast_large_sdu_bis_cfg cfg = { + .NSE = 10, + .Framed = 1, + .Framing_Mode = 0, + .BN = 5, + .SDU_Interval = 15000, + .ISO_Interval = 30000, + }; + + test_ial_broadcast_large_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_15_c) { + const struct test_ial_broadcast_large_sdu_bis_cfg cfg = { + .NSE = 6, + .Framed = 1, + .Framing_Mode = 0, + .BN = 3, + .SDU_Interval = 20000, + .ISO_Interval = 20000, + }; + + test_ial_broadcast_large_sdu_bis(&cfg); +} + +struct test_ial_broadcast_multiple_small_sdus_bis_cfg { + uint8_t NSE; + uint8_t BN; + uint8_t Max_PDU; + uint32_t SDU_Interval; + uint32_t ISO_Interval; +}; + +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; */ + const uint8_t LLID = 0b10; + const uint8_t Framed = 0x01; + const uint8_t Framing_Mode = 0; + int pdu_len; + uint8_t pdu[cfg->Max_PDU]; + uint32_t sdu_1_ts, sdu_2_ts, event_ts; + uint32_t timeoffset; + uint16_t seg_hdr; + uint8_t llid = 0xff; + 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); + + /* 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); + + /* 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); + + event_ts = sdu_2_ts + 200; + 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); + TEST_ASSERT(llid == LLID, "LLID is incorrect %d", llid); + + /* SDU 1 */ + seg = &pdu[0]; + TEST_ASSERT(pdu_len > 24, "PDU length is incorrect %d", pdu_len); + seg_hdr = get_le16(&seg[0]); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_SC(seg_hdr) == 0, + "SC is incorrect %d", BLE_LL_ISOAL_SEGHDR_SC(seg_hdr)); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr) == 1, + "SC is incorrect %d", BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr)); + seg_len = BLE_LL_ISOAL_SEGHDR_LEN(seg_hdr); + TEST_ASSERT(seg_len == 20 + 3, "Segment length is incorrect %d", pdu_len); + timeoffset = get_le24(&seg[2]); + TEST_ASSERT(timeoffset == event_ts - sdu_1_ts, + "Time offset is incorrect %d", timeoffset); + + /* SDU 1 */ + seg = &pdu[25]; + TEST_ASSERT(pdu_len == 55, "PDU length is incorrect %d", pdu_len); + seg_hdr = get_le16(&seg[0]); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_SC(seg_hdr) == 0, + "SC is incorrect %d", BLE_LL_ISOAL_SEGHDR_SC(seg_hdr)); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr) == 1, + "SC is incorrect %d", BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr)); + seg_len = BLE_LL_ISOAL_SEGHDR_LEN(seg_hdr); + TEST_ASSERT(seg_len == 25 + 3, "Segment length is incorrect %d", pdu_len); + timeoffset = get_le24(&seg[2]); + TEST_ASSERT(timeoffset == event_ts - sdu_2_ts, + "Time offset is incorrect %d", timeoffset); + + (void)ble_ll_isoal_mux_event_done(&mux); + + test_ial_teardown(&mux); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_17_c) { + const struct test_ial_broadcast_multiple_small_sdus_bis_cfg cfg = { + .NSE = 2, + .BN = 1, + .Max_PDU = 68, + .SDU_Interval = 500, + .ISO_Interval = 1000, + }; + + test_ial_broadcast_multiple_small_sdus_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_18_c) { + const struct test_ial_broadcast_multiple_small_sdus_bis_cfg cfg = { + .NSE = 2, + .BN = 1, + .Max_PDU = 68, + .SDU_Interval = 1000, + .ISO_Interval = 2000, + }; + + test_ial_broadcast_multiple_small_sdus_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_20_c) { + const struct test_ial_broadcast_multiple_small_sdus_bis_cfg cfg = { + .NSE = 4, + .BN = 2, + .Max_PDU = 65, + .SDU_Interval = 500, + .ISO_Interval = 2000, + }; + + test_ial_broadcast_multiple_small_sdus_bis(&cfg); +} + +struct test_ial_segmentation_header { + uint8_t SC; + uint8_t CMPLT; + uint8_t LENGTH; +}; +struct test_ial_broadcast_zero_length_sdu_bis_cfg { + uint8_t NSE; + uint8_t Framed; + uint8_t Framing_Mode; + uint8_t LLID; + uint8_t BN; + struct test_ial_segmentation_header Segmentation_Header; + bool Time_Offset; +}; + +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; + const uint32_t ISO_Interval = 10000; + const uint32_t SDU_Interval = 10000; + /* const uint16_t Max_SDU = 32; */ + const uint16_t Max_PDU = 32; + int pdu_len; + uint8_t pdu[Max_PDU]; + uint32_t timeoffset; + 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); + + /* The Upper Tester sends an HCI ISO Data packet to the IUT with zero data length. */ + test_sdu_enqueue(&mux, 0, 0, 100); + + 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 + * fields as specified in Table 4.35. Length is 0 if LLID is 0b00 + * 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); + TEST_ASSERT(llid == cfg->LLID, "LLID is incorrect %d", llid); + + if (cfg->LLID == 0b00) { + TEST_ASSERT(pdu_len == 0, "PDU length is incorrect %d", pdu_len); + } else if (cfg->LLID == 0b01) { + TEST_ASSERT(pdu_len == 5, "PDU length is incorrect %d", pdu_len); + } + + if (cfg->Framed) { + seg_hdr = get_le16(&pdu[0]); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_SC(seg_hdr) == cfg->Segmentation_Header.SC, + "SC is incorrect %d", BLE_LL_ISOAL_SEGHDR_SC(seg_hdr)); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr) == cfg->Segmentation_Header.CMPLT, + "CMPLT is incorrect %d", BLE_LL_ISOAL_SEGHDR_CMPLT(seg_hdr)); + TEST_ASSERT(BLE_LL_ISOAL_SEGHDR_LEN(seg_hdr) == cfg->Segmentation_Header.LENGTH, + "LENGTH is incorrect %d", BLE_LL_ISOAL_SEGHDR_LEN(seg_hdr)); + timeoffset = get_le24(&pdu[2]); + TEST_ASSERT(timeoffset == 400, "Time offset is incorrect %d", timeoffset); + } + + for (uint8_t idx = 1; idx < cfg->BN; idx++) { + 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); + + test_ial_teardown(&mux); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_25_c) { + const struct test_ial_broadcast_zero_length_sdu_bis_cfg cfg = { + .NSE = 6, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 2, + .Segmentation_Header.SC = 0, + .Segmentation_Header.CMPLT = 1, + .Segmentation_Header.LENGTH = 3, + .Time_Offset = true, + }; + + test_ial_broadcast_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_26_c) { + const struct test_ial_broadcast_zero_length_sdu_bis_cfg cfg = { + .NSE = 2, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 1, + .Segmentation_Header.SC = 0, + .Segmentation_Header.CMPLT = 1, + .Segmentation_Header.LENGTH = 3, + .Time_Offset = true, + }; + + test_ial_broadcast_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_27_c) { + const struct test_ial_broadcast_zero_length_sdu_bis_cfg cfg = { + .NSE = 4, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 1, + .Segmentation_Header.SC = 0, + .Segmentation_Header.CMPLT = 1, + .Segmentation_Header.LENGTH = 3, + .Time_Offset = true, + }; + + test_ial_broadcast_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_28_c) { + const struct test_ial_broadcast_zero_length_sdu_bis_cfg cfg = { + .NSE = 6, + .Framed = 1, + .Framing_Mode = 0, + .LLID = 0b10, + .BN = 3, + .Segmentation_Header.SC = 0, + .Segmentation_Header.CMPLT = 1, + .Segmentation_Header.LENGTH = 3, + .Time_Offset = true, + }; + + test_ial_broadcast_zero_length_sdu_bis(&cfg); +} + +TEST_CASE_SELF(test_ial_bis_fra_brd_bv_30_c) { + const struct test_ial_broadcast_zero_length_sdu_bis_cfg cfg = { + .NSE = 6, + .Framed = 1, + .Framing_Mode = 1, + .LLID = 0b10, + .BN = 2, + .Segmentation_Header.SC = 0, + .Segmentation_Header.CMPLT = 1, + .Segmentation_Header.LENGTH = 3, + .Time_Offset = true, + }; + + test_ial_broadcast_zero_length_sdu_bis(&cfg); +} + +TEST_SUITE(ble_ll_isoal_test_suite) { + os_mbuf_test_setup(); + + ble_ll_isoal_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_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_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(); + + ble_ll_isoal_reset(); +} diff --git a/nimble/controller/test/src/ble_ll_test.c b/nimble/controller/test/src/ble_ll_test.c index 2b36cb1f3..7bfbf9bd2 100644 --- a/nimble/controller/test/src/ble_ll_test.c +++ b/nimble/controller/test/src/ble_ll_test.c @@ -25,6 +25,7 @@ TEST_SUITE_DECL(ble_ll_aa_test_suite); TEST_SUITE_DECL(ble_ll_crypto_test_suite); TEST_SUITE_DECL(ble_ll_csa2_test_suite); +TEST_SUITE_DECL(ble_ll_isoal_test_suite); int main(int argc, char **argv) @@ -32,6 +33,7 @@ main(int argc, char **argv) ble_ll_aa_test_suite(); ble_ll_crypto_test_suite(); ble_ll_csa2_test_suite(); + ble_ll_isoal_test_suite(); return tu_any_failed; } diff --git a/nimble/controller/test/syscfg.yml b/nimble/controller/test/syscfg.yml index 6edad438b..f7a3c4ef5 100644 --- a/nimble/controller/test/syscfg.yml +++ b/nimble/controller/test/syscfg.yml @@ -18,8 +18,12 @@ syscfg.vals: BLE_LL_CFG_FEAT_LE_CSA2: 1 + BLE_LL_ISO: 1 + BLE_VERSION: 54 # Prevent priority conflict with controller task. MCU_TIMER_POLLER_PRIO: 1 MCU_UART_POLLER_PRIO: 2 NATIVE_SOCKETS_PRIO: 3 + + BLE_TRANSPORT_ISO_SIZE: 255 diff --git a/nimble/include/nimble/ble.h b/nimble/include/nimble/ble.h index a2add5d79..d5e41a37c 100644 --- a/nimble/include/nimble/ble.h +++ b/nimble/include/nimble/ble.h @@ -128,6 +128,8 @@ struct ble_mbuf_hdr_txinfo struct ble_mbuf_hdr_txiso { uint16_t packet_seq_num; + uint32_t cpu_timestamp; + uint32_t hci_timestamp; }; /** diff --git a/nimble/include/nimble/hci_common.h b/nimble/include/nimble/hci_common.h index f4fe5a7f8..f1d2eb46d 100644 --- a/nimble/include/nimble/hci_common.h +++ b/nimble/include/nimble/hci_common.h @@ -2466,6 +2466,10 @@ struct hci_data_hdr #define BLE_HCI_ISO_DATA_PATH_ID_HCI 0x00 +#define BLE_HCI_ISO_FRAMING_UNFRAMED 0x00 +#define BLE_HCI_ISO_FRAMING_FRAMED_SEGMENTABLE 0x01 +#define BLE_HCI_ISO_FRAMING_FRAMED_UNSEGMENTED 0x02 + struct ble_hci_iso { uint16_t handle; uint16_t length;
