diff --git a/nimble/controller/include/controller/ble_ll.h b/nimble/controller/include/controller/ble_ll.h index ab75edcba3..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) @@ -581,6 +632,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/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 fe70044234..ea00a7753f 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); @@ -235,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 a0d6dbbc03..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); @@ -1305,6 +1331,35 @@ 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; + +#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; +} + uint8_t ble_ll_tx_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) { @@ -1699,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(); @@ -1944,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; @@ -1989,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(); @@ -2033,7 +2097,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/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_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 774465315e..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); } @@ -652,7 +555,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; } @@ -667,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) { @@ -683,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; @@ -691,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'; @@ -717,29 +622,27 @@ 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; - 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; } @@ -751,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(); @@ -807,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) @@ -821,26 +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; - - if (big->interleaved) { - ble_phy_tifs_txtx_set(big->bis_spacing, 0); - } else { - ble_phy_tifs_txtx_set(big->sub_interval, 0); - } + 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); @@ -868,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)) { @@ -990,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; @@ -1011,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, @@ -1098,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); } @@ -1203,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); } } @@ -1510,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 0f73fd2e8c..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; @@ -1569,7 +1574,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..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 @@ -1206,8 +1218,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; } @@ -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 7f1ac96054..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 * @@ -930,17 +956,20 @@ 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; } TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link); - remove_cb(entry); entry->enqueued = 0; + remove_cb(entry); } if (first_removed) { @@ -1020,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(); @@ -1115,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) { @@ -1123,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 517cfe0fdf..0eb0c60214 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 @@ -522,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. @@ -680,11 +706,17 @@ 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': 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/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/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..0cc94286d9 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,9 +1361,18 @@ 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 + /* + * 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 @@ -1295,59 +1387,17 @@ 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]; - - 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 + 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); - /* 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 - * 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(); @@ -1593,9 +1643,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 +1969,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 +1978,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 +1986,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 +2282,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 +2421,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; } 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/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..2d70a6bc36 --- /dev/null +++ b/nimble/transport/ipc/icbmsg/src/icbmsg_ble_hci.c @@ -0,0 +1,212 @@ +/* + * 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); +} + +static int +icbmsg_ble_hci_iso_tx(struct os_mbuf *om) +{ + return icbmsg_ble_hci_send_mbuf(HCI_IPC_TYPE_ISO, om); +} + +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); +} + +int +ble_transport_to_hs_iso_impl(struct os_mbuf *om) +{ + return icbmsg_ble_hci_iso_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/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/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 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) {