This is an automated email from the ASF dual-hosted git repository.

andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git

commit 6d02d3406aff5f604ed5dff8f1c2e321bc3e1db8
Author: Andrzej Kaczmarek <andrzej.kaczma...@codecoup.pl>
AuthorDate: Sat Feb 26 21:37:39 2022 +0100

    nimble/ll: Initial support for ISO Broadcasting state
    
    This adds initial support for ISO Broadcasting state:
    - all valid combinations of BIG/BIS parameters should be supported,
    - encryption is supported,
    - BIG control procedures are supported,
    - no LE Create BIG command (only LE Create BIG Test).
    
    HCI and ISOAL support coming soon.
---
 nimble/controller/include/controller/ble_ll.h      |    4 +
 .../controller/include/controller/ble_ll_iso_big.h |   47 +
 .../controller/include/controller/ble_ll_sched.h   |    5 +
 nimble/controller/src/ble_ll.c                     |   14 +-
 nimble/controller/src/ble_ll_hci.c                 |   37 +-
 nimble/controller/src/ble_ll_iso_big.c             | 1092 ++++++++++++++++++++
 nimble/controller/src/ble_ll_sched.c               |   36 +
 nimble/controller/syscfg.yml                       |   17 +
 8 files changed, 1241 insertions(+), 11 deletions(-)

diff --git a/nimble/controller/include/controller/ble_ll.h 
b/nimble/controller/include/controller/ble_ll.h
index 5043429b..fcfdff20 100644
--- a/nimble/controller/include/controller/ble_ll.h
+++ b/nimble/controller/include/controller/ble_ll.h
@@ -240,6 +240,9 @@ extern STATS_SECT_DECL(ble_ll_stats) ble_ll_stats;
 #if MYNEWT_VAL(BLE_LL_EXT)
 #define BLE_LL_STATE_EXTERNAL       (8)
 #endif
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+#define BLE_LL_STATE_BIG            (9)
+#endif
 
 /* LL Features */
 #define BLE_LL_FEAT_LE_ENCRYPTION       (0x0000000001)
@@ -301,6 +304,7 @@ extern STATS_SECT_DECL(ble_ll_stats) ble_ll_stats;
 /* LL timing */
 #define BLE_LL_IFS                  (150)       /* usecs */
 #define BLE_LL_MAFS                 (300)       /* usecs */
+#define BLE_LL_MSS                  (150)       /* usecs */
 
 /*
  * BLE LL device address. Note that element 0 of the array is the LSB and
diff --git a/nimble/controller/include/controller/ble_ll_iso_big.h 
b/nimble/controller/include/controller/ble_ll_iso_big.h
new file mode 100644
index 00000000..1756fd26
--- /dev/null
+++ b/nimble/controller/include/controller/ble_ll_iso_big.h
@@ -0,0 +1,47 @@
+/*
+ * 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_
+#define H_BLE_LL_ISO_BIG_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+
+void ble_ll_iso_big_chan_map_update(void);
+
+void ble_ll_iso_big_halt(void);
+
+int ble_ll_iso_big_hci_create(const uint8_t *cmdbuf, uint8_t len);
+int ble_ll_iso_big_hci_create_test(const uint8_t *cmdbuf, uint8_t len);
+int ble_ll_iso_big_hci_terminate(const uint8_t *cmdbuf, uint8_t len);
+void ble_ll_iso_big_hci_evt_complete(void);
+
+void ble_ll_iso_big_init(void);
+void ble_ll_iso_big_reset(void);
+
+#endif /* BLE_LL_ISO_BROADCASTER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_BLE_LL_ISO_BIG_ */
diff --git a/nimble/controller/include/controller/ble_ll_sched.h 
b/nimble/controller/include/controller/ble_ll_sched.h
index 5e04ce09..961a20aa 100644
--- a/nimble/controller/include/controller/ble_ll_sched.h
+++ b/nimble/controller/include/controller/ble_ll_sched.h
@@ -70,6 +70,7 @@ extern uint8_t g_ble_ll_sched_offset_ticks;
 #define BLE_LL_SCHED_TYPE_PERIODIC  (6)
 #define BLE_LL_SCHED_TYPE_SYNC      (7)
 #define BLE_LL_SCHED_TYPE_SCAN_AUX  (8)
+#define BLE_LL_SCHED_TYPE_BIG       (9)
 #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);
 #endif
 #endif
 
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+int ble_ll_sched_iso_big(struct ble_ll_sched_item *sch, int first);
+#endif /* BLE_LL_ISO_BROADCASTER */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/nimble/controller/src/ble_ll.c b/nimble/controller/src/ble_ll.c
index a1bbe63b..de30b892 100644
--- a/nimble/controller/src/ble_ll.c
+++ b/nimble/controller/src/ble_ll.c
@@ -46,6 +46,7 @@
 #include "controller/ble_ll_trace.h"
 #include "controller/ble_ll_sync.h"
 #include "controller/ble_fem.h"
+#include "controller/ble_ll_iso_big.h"
 #if MYNEWT_VAL(BLE_LL_EXT)
 #include "controller/ble_ll_ext.h"
 #endif
@@ -1678,6 +1679,10 @@ ble_ll_reset(void)
     ble_fem_lna_init();
 #endif
 
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+    ble_ll_iso_big_reset();
+#endif
+
     /* Re-initialize the PHY */
     rc = ble_phy_init();
 
@@ -1913,8 +1918,11 @@ ble_ll_init(void)
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ISO)
     features |= BLE_LL_FEAT_CIS_CENTRAL;
     features |= BLE_LL_FEAT_CIS_PERIPH;
+    features |= BLE_LL_FEAT_CIS_HOST;
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
     features |= BLE_LL_FEAT_ISO_BROADCASTER;
-    features |= BLE_LL_FEAT_ISO_HOST_SUPPORT;
 #endif
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ENHANCED_CONN_UPDATE)
@@ -1942,6 +1950,10 @@ ble_ll_init(void)
     ble_ll_hci_vs_init();
 #endif
 
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+    ble_ll_iso_big_init();
+#endif
+
 #if MYNEWT_VAL(BLE_LL_EXT)
     ble_ll_ext_init();
 #endif
diff --git a/nimble/controller/src/ble_ll_hci.c 
b/nimble/controller/src/ble_ll_hci.c
index 6e3f0afa..b693f5ca 100644
--- a/nimble/controller/src/ble_ll_hci.c
+++ b/nimble/controller/src/ble_ll_hci.c
@@ -35,6 +35,7 @@
 #include "controller/ble_ll_sync.h"
 #include <controller/ble_ll_utils.h>
 #include "controller/ble_ll_iso.h"
+#include "controller/ble_ll_iso_big.h"
 #include "ble_ll_priv.h"
 #include "ble_ll_conn_priv.h"
 #include "ble_ll_hci_priv.h"
@@ -338,7 +339,7 @@ ble_ll_hci_le_read_bufsize(uint8_t *rspbuf, uint8_t *rsplen)
     return BLE_ERR_SUCCESS;
 }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ISO)
+#if MYNEWT_VAL(BLE_LL_ISO)
 /**
  * HCI read buffer size v2 command. Returns the ACL and ISO data packet length 
and
  * num data packets.
@@ -355,8 +356,9 @@ ble_ll_hci_le_read_bufsize_v2(uint8_t *rspbuf, uint8_t 
*rsplen)
 
     rp->data_len = htole16(g_ble_ll_data.ll_acl_pkt_size);
     rp->data_packets = g_ble_ll_data.ll_num_acl_pkts;
-    rp->iso_data_len = 0;
-    rp->iso_data_packets = 0;
+    /* XXX for testing only */
+    rp->iso_data_len = 512;
+    rp->iso_data_packets = 10;
 
     *rsplen = sizeof(*rp);
     return BLE_ERR_SUCCESS;
@@ -661,6 +663,11 @@ ble_ll_hci_le_cmd_send_cmd_status(uint16_t ocf)
     case BLE_HCI_OCF_LE_GEN_DHKEY:
     case BLE_HCI_OCF_LE_SET_PHY:
     case BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC:
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+    case BLE_HCI_OCF_LE_CREATE_BIG:
+    case BLE_HCI_OCF_LE_CREATE_BIG_TEST:
+    case BLE_HCI_OCF_LE_TERMINATE_BIG:
+#endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_SCA_UPDATE)
     case BLE_HCI_OCF_LE_REQ_PEER_SCA:
 #endif
@@ -862,6 +869,9 @@ ble_ll_hci_le_set_host_chan_class(const uint8_t *cmdbuf, 
uint8_t len)
     g_ble_ll_data.chan_map_used = chan_map_used;
 
     ble_ll_conn_chan_map_update();
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+    ble_ll_iso_big_chan_map_update();
+#endif
 
     return BLE_ERR_SUCCESS;
 }
@@ -1254,6 +1264,17 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t 
len, uint16_t ocf,
         rc = ble_ll_set_default_sync_transfer_params(cmdbuf, len);
         break;
 #endif
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+    case BLE_HCI_OCF_LE_CREATE_BIG:
+        rc = ble_ll_iso_big_hci_create(cmdbuf, len);
+        break;
+    case BLE_HCI_OCF_LE_CREATE_BIG_TEST:
+        rc = ble_ll_iso_big_hci_create_test(cmdbuf, len);
+        break;
+    case BLE_HCI_OCF_LE_TERMINATE_BIG:
+        rc = ble_ll_iso_big_hci_terminate(cmdbuf, len);
+        break;
+#endif /* BLE_LL_ISO_BROADCASTER */
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ISO)
     case BLE_HCI_OCF_LE_READ_ISO_TX_SYNC:
         rc = ble_ll_iso_read_tx_sync(cmdbuf, len);
@@ -1273,12 +1294,6 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t 
len, uint16_t ocf,
     case BLE_HCI_OCF_LE_REJECT_CIS_REQ:
         rc = ble_ll_iso_reject_cis_req(cmdbuf, len);
         break;
-    case BLE_HCI_OCF_LE_CREATE_BIG:
-        rc = ble_ll_iso_create_big(cmdbuf, len);
-        break;
-    case BLE_HCI_OCF_LE_TERMINATE_BIG:
-        rc = ble_ll_iso_terminate_big(cmdbuf, len);
-        break;
     case BLE_HCI_OCF_LE_BIG_CREATE_SYNC:
         rc = ble_ll_iso_big_create_sync(cmdbuf, len);
         break;
@@ -1291,12 +1306,14 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t 
len, uint16_t ocf,
     case BLE_HCI_OCF_LE_REMOVE_ISO_DATA_PATH:
         rc = ble_ll_iso_remove_iso_data_path(cmdbuf, len);
         break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ISO)
     case BLE_HCI_OCF_LE_RD_BUF_SIZE_V2:
         if (len == 0) {
             rc = ble_ll_hci_le_read_bufsize_v2(rspbuf, rsplen);
         }
         break;
-#endif
+#endif /* BLE_LL_ISO */
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ISO_TEST)
     case BLE_HCI_OCF_LE_SET_CIG_PARAM_TEST:
         rc = ble_ll_iso_set_cig_param_test(cmdbuf, len, rspbuf, rsplen);
diff --git a/nimble/controller/src/ble_ll_iso_big.c 
b/nimble/controller/src/ble_ll_iso_big.c
new file mode 100644
index 00000000..20e5ff07
--- /dev/null
+++ b/nimble/controller/src/ble_ll_iso_big.c
@@ -0,0 +1,1092 @@
+/*
+ * 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 <errno.h>
+#include <stdint.h>
+#include <syscfg/syscfg.h>
+#include <nimble/ble.h>
+#include <nimble/hci_common.h>
+#include <nimble/transport.h>
+#include <controller/ble_ll.h>
+#include <controller/ble_ll_crypto.h>
+#include <controller/ble_ll_hci.h>
+#include <controller/ble_ll_iso_big.h>
+#include <controller/ble_ll_pdu.h>
+#include <controller/ble_ll_sched.h>
+#include <controller/ble_ll_rfmgmt.h>
+#include <controller/ble_ll_tmr.h>
+#include <controller/ble_ll_utils.h>
+#include <controller/ble_ll_whitelist.h>
+#include "ble_ll_priv.h"
+
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+
+/* XXX make those configurable */
+#define BIG_POOL_SIZE           (10)
+#define BIS_POOL_SIZE           (10)
+
+#define BIG_HANDLE_INVALID      (0xff)
+
+#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;
+    uint16_t conn_handle;
+
+    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;
+
+    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 */
+    uint8_t irc; /* 1-15  */
+    uint8_t pto; /* 0-15, mandatory 0 */
+    uint32_t sdu_interval;
+    uint16_t iso_interval;
+    uint16_t max_transport_latency;
+    uint16_t max_sdu;
+    uint8_t max_pdu;
+    uint8_t phy;
+    uint8_t interleaved : 1;
+    uint8_t framed : 1;
+    uint8_t encrypted : 1;
+    uint8_t broadcast_code[16];
+};
+
+struct ble_ll_iso_big {
+    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 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;
+
+    uint64_t big_counter;
+    uint64_t bis_counter;
+
+    uint32_t sync_delay;
+    uint32_t event_start;
+    uint8_t event_start_us;
+    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;
+
+    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 void
+ble_ll_iso_big_free(struct ble_ll_iso_big *big)
+{
+    struct ble_ll_iso_bis *bis;
+
+    if (big->handle == BIG_HANDLE_INVALID) {
+        return;
+    }
+
+    big->handle = BIG_HANDLE_INVALID;
+    ble_ll_sched_rmv_elem(&big->sch);
+    ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &big->event_done);
+
+    STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) {
+        bis->big = NULL;
+        bis_pool_free++;
+    }
+
+    big_pool_free++;
+}
+
+static void
+ble_ll_iso_big_terminate_complete(struct ble_ll_iso_big *big)
+{
+    struct ble_hci_ev *hci_ev;
+    struct ble_hci_ev_le_subev_terminate_big_complete *evt;
+    uint8_t big_handle;
+    uint8_t reason;
+
+    big_handle = big->handle;
+    reason = big->term_reason;
+
+    ble_ll_iso_big_free(big);
+
+    hci_ev = ble_transport_alloc_evt(0);
+    if (!hci_ev) {
+        BLE_LL_ASSERT(0);
+        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_TERMINATE_BIG_COMPLETE;
+    evt->big_handle = big_handle;
+    evt->reason = reason;
+
+    ble_transport_to_hs_evt(hci_ev);
+}
+
+static void
+ble_ll_iso_big_chan_map_update_complete(struct ble_ll_iso_big *big)
+{
+    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);
+}
+
+static void
+ble_ll_iso_big_update_event_start(struct ble_ll_iso_big *big)
+{
+    os_sr_t sr;
+
+    OS_ENTER_CRITICAL(sr);
+    big->event_start = big->sch.start_time + g_ble_ll_sched_offset_ticks;
+    big->event_start_us = big->sch.remainder;
+    OS_EXIT_CRITICAL(sr);
+}
+
+static void
+ble_ll_iso_big_event_done(struct ble_ll_iso_big *big)
+{
+    int rc;
+
+    ble_ll_rfmgmt_release();
+
+    if (big->big_counter == 0) {
+        BLE_LL_ASSERT(big == big_pending);
+        ble_ll_iso_big_hci_evt_complete();
+    }
+
+
+    big->sch.start_time = big->event_start;
+    big->sch.remainder = big->event_start_us;
+
+    do {
+        big->big_counter++;
+        big->bis_counter += big->bn;
+
+        if (big->control_active &&
+            (big->control_instant == (uint16_t)big->big_counter)) {
+            switch (big->control_active) {
+            case BIG_CONTROL_ACTIVE_TERM:
+                ble_ll_iso_big_terminate_complete(big);
+                return;
+            case BIG_CONTROL_ACTIVE_CHAN_MAP:
+                ble_ll_iso_big_chan_map_update_complete(big);
+                break;
+            default:
+                BLE_LL_ASSERT(0);
+                break;
+            }
+
+            big->control_active = 0;
+            big->cstf = 0;
+        }
+
+        if (!big->control_active) {
+            if (big->term_pending) {
+                big->term_pending = 0;
+                big->control_active = BIG_CONTROL_ACTIVE_TERM;
+            } else if (big->chan_map_new_pending) {
+                memcpy(big->chan_map_new, g_ble_ll_data.chan_map,
+                       BLE_LL_CHAN_MAP_LEN);
+                big->chan_map_new_pending = 0;
+                big->control_active = BIG_CONTROL_ACTIVE_CHAN_MAP;
+            }
+
+            if (big->control_active) {
+                big->control_instant = big->big_counter + 6;
+                big->cstf = 1;
+                big->cssn += 1;
+            }
+        }
+
+        /* XXX precalculate some data here? */
+
+        ble_ll_tmr_add(&big->sch.start_time, &big->sch.remainder,
+                       big->iso_interval * 1250);
+        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->control_active) {
+            /* XXX fixme */
+            big->sch.end_time += 10;
+        }
+
+        /* XXX this should always succeed since we preempt anything for now */
+        rc = ble_ll_sched_iso_big(&big->sch, 0);
+        assert(rc == 0);
+    } while (rc < 0);
+
+    ble_ll_iso_big_update_event_start(big);
+}
+
+static void
+ble_ll_iso_big_event_done_ev(struct ble_npl_event *ev)
+{
+    struct ble_ll_iso_big *big;
+
+    big = ble_npl_event_get_arg(ev);
+
+    ble_ll_iso_big_event_done(big);
+}
+
+static void
+ble_ll_iso_big_event_done_to_ll(struct ble_ll_iso_big *big)
+{
+    big_active = NULL;
+    ble_ll_state_set(BLE_LL_STATE_STANDBY);
+    ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &big->event_done);
+}
+
+static uint8_t
+ble_ll_iso_big_control_pdu_cb(uint8_t *dptr, void *arg, uint8_t *hdr_byte)
+{
+    struct ble_ll_iso_big *big;
+    uint8_t len;
+
+    big = arg;
+
+    /* CSTF shall be set to 0 in Control PDU */
+    *hdr_byte = 3 | (big->cssn << 2);
+
+    BLE_LL_ASSERT(big->cstf);
+    BLE_LL_ASSERT(big->control_active);
+
+    if (big->encrypted) {
+        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);
+    }
+
+    switch (big->control_active) {
+    case BIG_CONTROL_ACTIVE_CHAN_MAP:
+        dptr[0] = 0x00; /* BIG_CHANNEL_MAP_IND */
+        memcpy(&dptr[1], big->chan_map_new, BLE_LL_CHAN_MAP_LEN);
+        put_le16(&dptr[6], big->control_instant);
+        len = 8;
+        break;
+    case BIG_CONTROL_ACTIVE_TERM:
+        dptr[0] = 0x01; /* BIG_TERMINATE_IND */
+        dptr[1] = big->term_reason;
+        put_le16(&dptr[2], big->control_instant);
+        len = 4;
+        break;
+    default:
+        BLE_LL_ASSERT(0);
+        len = 0;
+        break;
+    }
+
+    return len;
+}
+
+static void
+ble_ll_iso_big_control_txend_cb(void *arg)
+{
+    struct ble_ll_iso_big *big;
+
+    big = arg;
+
+    ble_ll_iso_big_event_done_to_ll(big);
+}
+
+static int
+ble_ll_iso_big_control_tx(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);
+
+    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);
+
+    return rc;
+}
+
+static uint8_t
+ble_ll_iso_big_subevent_pdu_cb(uint8_t *dptr, void *arg, uint8_t *hdr_byte)
+{
+    struct ble_ll_iso_big *big;
+    struct ble_ll_iso_bis *bis;
+    int idx;
+    uint8_t llid;
+    uint8_t pdu_len;
+
+    big = arg;
+    bis = big->tx.bis;
+
+    /* Core 5.3, Vol 6, Part B, 4.4.6.6 */
+    if (bis->tx.g < big->irc) {
+        idx = bis->tx.n;
+    } else {
+        idx = big->bn * big->pto * (bis->tx.g - big->irc + 1) + bis->tx.n;
+    }
+
+    if (big->encrypted) {
+        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 + idx, 1);
+    }
+
+    llid = 0;
+    pdu_len = big->max_pdu;
+    /* XXX dummy data for testing */
+    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[11] = 'B';
+    } else if (bis->tx.g < big->irc) {
+        dptr[11] = 'R';
+    } else {
+        dptr[11] = 'P';
+    }
+    dptr[12] = 0xff;
+
+    *hdr_byte = llid | (big->cssn << 2) | (big->cstf << 5);
+
+    return pdu_len;
+}
+
+static int
+ble_ll_iso_big_subevent_tx(struct ble_ll_iso_big *big)
+{
+    struct ble_ll_iso_bis *bis;
+    uint16_t chan_idx;
+    int to_tx;
+    int rc;
+
+    bis = big->tx.bis;
+
+    if (bis->tx.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);
+    } else {
+        chan_idx = ble_ll_utils_dci_iso_subevent(bis->chan_id,
+                                                 &bis->tx.prn_sub_lu,
+                                                 big->chan_map_used,
+                                                 big->chan_map,
+                                                 &bis->tx.remap_idx);
+    }
+
+    ble_phy_setchan(chan_idx, bis->aa, bis->crc_init);
+
+    to_tx = (big->tx.subevents_rem > 1) || big->cstf;
+
+    rc = ble_phy_tx(ble_ll_iso_big_subevent_pdu_cb, big,
+                    to_tx ? BLE_PHY_TRANSITION_TX_TX
+                          : BLE_PHY_TRANSITION_NONE);
+    return rc;
+}
+
+static void
+ble_ll_iso_big_subevent_txend_cb(void *arg)
+{
+    struct ble_ll_iso_big *big;
+    struct ble_ll_iso_bis *bis;
+    int rc;
+
+    big = arg;
+    bis = big->tx.bis;
+
+    bis->tx.n++;
+    if (bis->tx.n == big->bn) {
+        bis->tx.n = 0;
+        bis->tx.g++;
+    }
+
+    /* Switch to next BIS if interleaved or all subevents for current BIS were
+     * transmitted.
+     */
+    if (big->interleaved || (bis->tx.subevent_num == big->nse)) {
+        bis = STAILQ_NEXT(bis, bis_q_next);
+        if (!bis) {
+            bis = STAILQ_FIRST(&big->bis_q);
+        }
+        big->tx.bis = bis;
+    }
+
+    bis->tx.subevent_num++;
+    big->tx.subevents_rem--;
+
+    if (big->tx.subevents_rem > 0) {
+        rc = ble_ll_iso_big_subevent_tx(big);
+        if (rc) {
+            ble_phy_disable();
+            ble_ll_iso_big_event_done_to_ll(big);
+        }
+        return;
+    }
+
+    if (big->cstf) {
+        rc = ble_ll_iso_big_control_tx(big);
+        if (rc) {
+            ble_phy_disable();
+            ble_ll_iso_big_event_done_to_ll(big);
+        }
+        return;
+    }
+
+    ble_ll_iso_big_event_done_to_ll(big);
+}
+
+static int
+ble_ll_iso_big_event_sched_cb(struct ble_ll_sched_item *sch)
+{
+    struct ble_ll_iso_big *big;
+    struct ble_ll_iso_bis *bis;
+#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
+    uint8_t phy_mode;
+#endif
+    int rc;
+
+    big = sch->cb_arg;
+
+    ble_ll_state_set(BLE_LL_STATE_BIG);
+    big_active = big;
+
+    ble_ll_whitelist_disable();
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    ble_phy_resolv_list_disable();
+#endif
+#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
+    phy_mode = ble_ll_phy_to_phy_mode(big->phy, 0);
+    ble_phy_mode_set(phy_mode, phy_mode);
+#endif
+
+    BLE_LL_ASSERT(!big->framed);
+
+    /* XXX calculate this in advance at the end of previous event? */
+    big->tx.subevents_rem = big->num_bis * big->nse;
+    STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) {
+        bis->tx.subevent_num = 0;
+        bis->tx.n = 0;
+        bis->tx.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);
+    }
+
+    rc = ble_phy_tx_set_start_time(sch->start_time + 
g_ble_ll_sched_offset_ticks,
+                                   sch->remainder);
+    if (rc) {
+        ble_phy_disable();
+        ble_ll_iso_big_event_done_to_ll(big);
+        return BLE_LL_SCHED_STATE_DONE;
+    }
+
+    ble_phy_set_txend_cb(ble_ll_iso_big_subevent_txend_cb, big);
+
+    if (big->encrypted) {
+        ble_phy_encrypt_enable(big->gsk);
+    } else {
+        ble_phy_encrypt_disable();
+    }
+
+    rc = ble_ll_iso_big_subevent_tx(big);
+    if (rc) {
+        ble_phy_disable();
+        ble_ll_iso_big_event_done_to_ll(big);
+        return BLE_LL_SCHED_STATE_DONE;
+    }
+
+    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_big *big = NULL;
+    struct ble_ll_iso_bis *bis;
+    uint32_t seed_aa;
+    uint8_t pte;
+    uint8_t gc;
+    uint8_t idx;
+    int rc;
+
+    if ((big_pool_free == 0) || (bis_pool_free < num_bis)) {
+        return -ENOMEM;
+    }
+
+    /* Find free BIG */
+    for (idx = 0; idx < BIG_POOL_SIZE; idx++) {
+        if (!big && big_pool[idx].handle == BIG_HANDLE_INVALID) {
+            big = &big_pool[idx];
+        }
+        if (big_pool[idx].handle == big_handle) {
+            return -EALREADY;
+        }
+    }
+
+    BLE_LL_ASSERT(big);
+
+    /* TODO find valid advertising instance */
+
+    big->handle = big_handle;
+
+    big->crc_init = ble_ll_rand();
+    memcpy(big->chan_map, g_ble_ll_data.chan_map, BLE_LL_CHAN_MAP_LEN);
+    big->chan_map_used = g_ble_ll_data.chan_map_used;
+
+    big->big_counter = 0;
+    big->bis_counter = 0;
+
+    big->cstf = 0;
+    big->cssn = 0;
+    big->control_active = 0;
+    big->control_instant = 0;
+    big->chan_map_new_pending = 0;
+    big->term_pending = 0;
+    big->term_reason = 0;
+
+    /* Calculate number of additional events for pre-transmissions */
+    /* Core 5.3, Vol 6, Part B, 4.4.6.6 */
+    gc = bp->nse / bp->bn;
+    (void)pte;
+    if (bp->irc == gc) {
+        pte = 0;
+    } else {
+        pte = bp->pto * (gc - bp->irc);
+    }
+
+    /* Allocate BISes */
+    STAILQ_INIT(&big->bis_q);
+    big->num_bis = 0;
+    for (idx = 0; (big->num_bis < num_bis) && (idx < BIS_POOL_SIZE); idx++) {
+        bis = &bis_pool[idx];
+        if (bis->big) {
+            continue;
+        }
+
+        big->num_bis++;
+        STAILQ_INSERT_TAIL(&big->bis_q, bis, bis_q_next);
+
+        bis->big = big;
+        bis->num = big->num_bis;
+        bis->crc_init = (big->crc_init << 8) | (big->num_bis);
+
+        BLE_LL_ASSERT(!big->framed);
+    }
+
+    big_pool_free--;
+    bis_pool_free -= num_bis;
+
+    /* Calculate AA for each BIS and BIG Control. We have to repeat this 
process
+     * until all AAs are valid.
+     */
+    do {
+        rc = 0;
+
+        seed_aa = ble_ll_utils_calc_seed_aa();
+        big->ctrl_aa = ble_ll_utils_calc_big_aa(seed_aa, 0);
+
+        if (!ble_ll_utils_verify_aa(big->ctrl_aa)) {
+            continue;
+        }
+
+        rc = 1;
+
+        STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) {
+            bis->aa = ble_ll_utils_calc_big_aa(seed_aa, bis->num);
+            if (!ble_ll_utils_verify_aa(bis->aa)) {
+                rc = 0;
+                break;
+            }
+            bis->chan_id = bis->aa ^ (bis->aa >> 16);
+        }
+    } while (rc == 0);
+
+    big->bn = bp->bn;
+    big->pto = bp->pto;
+    big->irc = bp->irc;
+    big->nse = bp->nse;
+    big->interleaved = bp->interleaved;
+    big->framed = bp->framed;
+    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,
+                                                    
BLE_HCI_LE_PHY_CODED_S8_PREF));
+
+    /* Core 5.3, Vol 6, Part B, 4.4.6.4 */
+    if (big->interleaved) {
+        big->bis_spacing = big->mpt + BLE_LL_MSS;
+        big->sub_interval = big->num_bis * big->bis_spacing;
+    } else {
+        big->sub_interval = big->mpt + BLE_LL_MSS;
+        big->bis_spacing = big->nse * big->sub_interval;
+    }
+    /* Core 5.3, Vol 6, Part B, 4.4.6.5 */
+    big->sync_delay = (big->num_bis - 1) * big->bis_spacing +
+                      (big->nse - 1) * big->sub_interval + big->mpt;
+
+    /* Reset PTO if pre-transmissions won't be used */
+    big->gc = gc;
+    if (big->irc == gc) {
+        big->pto = 0;
+    }
+
+    if (big->encrypted) {
+        ble_ll_iso_big_calculate_gsk(big, bp->broadcast_code);
+        ble_ll_iso_big_calculate_iv(big);
+    }
+
+    /* For now we will schedule complete event as single item. This allows for
+     * shortest possible subevent space (150us) but can create sequence of long
+     * events that will block scheduler from other activities. To mitigate this
+     * we use preempt_none strategy so scheudling is opportunistic and 
depending
+     * on other activities this may create gaps in stream.
+     * Eventually we should allow for some more robust scheduling, e.g. per-BIS
+     * for sequential arrangement or per-subevent for interleaved, or event
+     * per-BIS-subevent but this requires larger subevent space since 150us is
+     * not enough for some phys to run scheduler item.
+     */
+
+    /* Schedule 1st event a bit in future */
+    /* FIXME schedule 6ms in the future to avoid conflict with periodic
+     *       advertising when both are started at the same time; we should
+     *       select this value in some smart way later...
+     */
+    big->sch.start_time = ble_ll_tmr_get() + ble_ll_tmr_u2t(6000);
+    big->sch.remainder = 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;
+
+    rc = ble_ll_sched_iso_big(&big->sch, 1);
+    if (rc < 0) {
+        ble_ll_iso_big_free(big);
+        return -EFAULT;
+    }
+
+    ble_ll_iso_big_update_event_start(big);
+
+    big_pending = big;
+
+    return 0;
+}
+
+static int
+ble_ll_iso_big_terminate(uint8_t big_handle, uint8_t reason)
+{
+    struct ble_ll_iso_big *big = NULL;
+    unsigned idx;
+
+    for (idx = 0; idx < BIG_POOL_SIZE; idx++) {
+        if (big_pool[idx].handle == big_handle) {
+            big = &big_pool[idx];
+            break;
+        }
+    }
+
+    if (!big) {
+        return -ENOENT;
+    }
+
+    big->term_reason = reason;
+    big->term_pending = 1;
+
+    return 0;
+}
+
+void
+ble_ll_iso_big_chan_map_update(void)
+{
+    struct ble_ll_iso_big *big;
+    int idx;
+
+    for (idx = 0; idx < BIG_POOL_SIZE; idx++) {
+        big = &big_pool[idx];
+
+        if (big->handle == BIG_HANDLE_INVALID) {
+            continue;
+        }
+
+        big->chan_map_new_pending = 1;
+    }
+}
+
+void
+ble_ll_iso_big_halt(void)
+{
+    if (big_active) {
+        ble_ll_iso_big_event_done_to_ll(big_active);
+    }
+}
+
+void
+ble_ll_iso_big_hci_evt_complete(void)
+{
+    struct ble_ll_iso_big *big;
+    struct ble_ll_iso_bis *bis;
+    struct ble_hci_ev_le_subev_create_big_complete *evt;
+    struct ble_hci_ev *hci_ev;
+    uint8_t idx;
+
+    big = big_pending;
+    big_pending = NULL;
+
+    if (!big) {
+        return;
+    }
+
+    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_CREATE_BIG_COMPLETE;
+    evt->status = 0x00;
+
+    evt->big_handle = big->handle;
+    put_le24(evt->big_sync_delay, big->sync_delay);
+    /* 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->phy = big->phy;
+    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);
+}
+
+int
+ble_ll_iso_big_hci_create(const uint8_t *cmdbuf, uint8_t len)
+{
+    /* TODO implement :) */
+
+    return BLE_ERR_UNSPECIFIED;
+}
+
+int
+ble_ll_iso_big_hci_create_test(const uint8_t *cmdbuf, uint8_t len)
+{
+    const struct ble_hci_le_create_big_test_cp *cmd = (void *)cmdbuf;
+    struct big_params bp;
+    uint32_t iso_interval_us;
+    int rc;
+
+    if (len != sizeof(*cmd)) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    if (!IN_RANGE(cmd->big_handle, 0x00, 0xef) ||
+        !IN_RANGE(cmd->adv_handle, 0x00, 0xef) ||
+        !IN_RANGE(cmd->num_bis, 0x01, 0x1f) ||
+        !IN_RANGE(get_le24(cmd->sdu_interval), 0x0000ff, 0x0fffff) ||
+        !IN_RANGE(le16toh(cmd->iso_interval), 0x0004, 0x0c80) ||
+        !IN_RANGE(cmd->nse, 0x01, 0x1f) ||
+        !IN_RANGE(le16toh(cmd->max_sdu), 0x0001, 0x0fff) ||
+        !IN_RANGE(le16toh(cmd->max_pdu), 0x0001, 0x00fb) ||
+        /* phy */
+        (cmd->packing > 1) || (cmd->framing > 1) ||
+        !IN_RANGE(cmd->bn, 0x01, 0x07) ||
+        !IN_RANGE(cmd->irc, 0x01, 0x0f) ||
+        !IN_RANGE(cmd->pto, 0x00, 0x0f) ||
+        (cmd->encryption) > 1) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    bp.nse = cmd->nse;
+    bp.bn = cmd->bn;
+    bp.irc = cmd->irc;
+    bp.pto = cmd->pto;
+    bp.sdu_interval = get_le24(cmd->sdu_interval);
+    bp.iso_interval = le16toh(cmd->iso_interval);
+    bp.max_sdu = le16toh(cmd->max_sdu);
+    bp.max_pdu = le16toh(cmd->max_pdu);
+    /* TODO verify phy */
+    if (cmd->phy & BLE_HCI_LE_PHY_2M_PREF_MASK) {
+        bp.phy = BLE_PHY_2M;
+    } else if (cmd->phy & BLE_HCI_LE_PHY_1M_PREF_MASK) {
+        bp.phy = BLE_PHY_1M;
+    } else {
+        bp.phy = BLE_PHY_CODED;
+    }
+    bp.interleaved = cmd->packing;
+    bp.framed = cmd->framing;
+    bp.encrypted = cmd->encryption;
+    memcpy(bp.broadcast_code, cmd->broadcast_code, 16);
+
+    if (bp.nse % bp.bn) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    iso_interval_us = bp.iso_interval * 1250;
+
+    if (!bp.framed) {
+        /* sdu_interval shall be an integer multiple of iso_interval */
+        if (iso_interval_us % bp.sdu_interval) {
+            return BLE_ERR_INV_HCI_CMD_PARMS;
+        }
+
+        /* bn shall be an integer multiple of (iso_interval / sdu_interval) */
+        if (bp.bn % (iso_interval_us / bp.sdu_interval)) {
+            return BLE_ERR_INV_HCI_CMD_PARMS;
+        }
+    }
+
+    rc = ble_ll_iso_big_create(cmd->big_handle, cmd->adv_handle, cmd->num_bis,
+                               &bp);
+    switch (rc) {
+    case 0:
+        break;
+    case -EINVAL:
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    case -ENOMEM:
+        return BLE_ERR_CONN_REJ_RESOURCES;
+    case -ENOENT:
+        return BLE_ERR_UNK_ADV_INDENT;
+    default:
+        return BLE_ERR_UNSPECIFIED;
+    }
+
+    return 0;
+}
+
+int
+ble_ll_iso_big_hci_terminate(const uint8_t *cmdbuf, uint8_t len)
+{
+    const struct ble_hci_le_terminate_big_cp *cmd = (void *)cmdbuf;
+    int err;
+
+    err = ble_ll_iso_big_terminate(cmd->big_handle, cmd->reason);
+    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_init(void)
+{
+    struct ble_ll_iso_big *big;
+    struct ble_ll_iso_bis *bis;
+    uint8_t idx;
+
+    memset(big_pool, 0, sizeof(big_pool));
+    memset(bis_pool, 0, sizeof(bis_pool));
+
+    for (idx = 0; idx < BIG_POOL_SIZE; idx++) {
+        big = &big_pool[idx];
+
+        big->handle = BIG_HANDLE_INVALID;
+
+        big->sch.sched_type = BLE_LL_SCHED_TYPE_BIG;
+        big->sch.sched_cb = ble_ll_iso_big_event_sched_cb;
+        big->sch.cb_arg = big;
+
+        ble_npl_event_init(&big->event_done, ble_ll_iso_big_event_done_ev, 
big);
+    }
+
+    for (idx = 0; idx < BIS_POOL_SIZE; idx++) {
+        bis = &bis_pool[idx];
+        bis->conn_handle = BLE_LL_CONN_HANDLE(BLE_LL_CONN_HANDLE_TYPE_BIS, 
idx);
+    }
+
+    big_pool_free = ARRAY_SIZE(big_pool);
+    bis_pool_free = ARRAY_SIZE(bis_pool);
+}
+
+void
+ble_ll_iso_big_reset(void)
+{
+    struct ble_ll_iso_big *big;
+    int idx;
+
+    for (idx = 0; idx < BIG_POOL_SIZE; idx++) {
+        big = &big_pool[idx];
+        ble_ll_iso_big_free(big);
+    }
+
+    ble_ll_iso_big_init();
+}
+
+#endif /* BLE_LL_ISO_BROADCASTER */
diff --git a/nimble/controller/src/ble_ll_sched.c 
b/nimble/controller/src/ble_ll_sched.c
index b31d7818..c4cbe142 100644
--- a/nimble/controller/src/ble_ll_sched.c
+++ b/nimble/controller/src/ble_ll_sched.c
@@ -32,6 +32,7 @@
 #include "controller/ble_ll_trace.h"
 #include "controller/ble_ll_tmr.h"
 #include "controller/ble_ll_sync.h"
+#include "controller/ble_ll_iso_big.h"
 #if MYNEWT_VAL(BLE_LL_EXT)
 #include "controller/ble_ll_ext.h"
 #endif
@@ -171,6 +172,12 @@ ble_ll_sched_preempt(struct ble_ll_sched_item *sch,
 #endif
 #endif
 #endif
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+        case BLE_LL_SCHED_TYPE_BIG:
+            /* FIXME sometimes it may be useful to preempt... */
+            BLE_LL_ASSERT(0);
+            break;
+#endif
 #if MYNEWT_VAL(BLE_LL_EXT)
             case BLE_LL_SCHED_TYPE_EXTERNAL:
                 ble_ll_ext_sched_removed(entry);
@@ -883,6 +890,30 @@ ble_ll_sched_adv_resched_pdu(struct ble_ll_sched_item *sch)
     return rc;
 }
 
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+int
+ble_ll_sched_iso_big(struct ble_ll_sched_item *sch, int first)
+{
+    os_sr_t sr;
+    int rc;
+
+    OS_ENTER_CRITICAL(sr);
+
+    if (first) {
+        rc = ble_ll_sched_insert(sch, BLE_LL_SCHED_MAX_DELAY_ANY, 
preempt_none);
+    } else {
+        /* XXX provide better strategy for preemption */
+        rc = ble_ll_sched_insert(sch, 0, preempt_any);
+    }
+
+    OS_EXIT_CRITICAL(sr);
+
+    ble_ll_sched_restart();
+
+    return rc;
+}
+#endif /* BLE_LL_ISO_BROADCASTER */
+
 /**
  * Remove a schedule element
  *
@@ -1023,6 +1054,11 @@ ble_ll_sched_execute_item(struct ble_ll_sched_item *sch)
         ble_ll_conn_event_halt();
         break;
 #endif
+#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
+    case BLE_LL_STATE_BIG:
+        ble_ll_iso_big_halt();
+        break;
+#endif
 #if MYNEWT_VAL(BLE_LL_EXT)
     case BLE_LL_STATE_EXTERNAL:
         ble_ll_ext_halt();
diff --git a/nimble/controller/syscfg.yml b/nimble/controller/syscfg.yml
index a7e6af03..f02b04df 100644
--- a/nimble/controller/syscfg.yml
+++ b/nimble/controller/syscfg.yml
@@ -472,6 +472,23 @@ syscfg.defs:
             Enable support for runtime antenna selection in FEM.
         value: 0
 
+    BLE_LL_ISO:
+        description: >
+            Enable support for isochronous data.
+            This enables common code (e.g. ISOAL) for all types of isochronous
+            data, particular ISO features have to be enabled separately.
+        restrictions:
+            - (BLE_VERSION >= 52) if 1
+        value: 0
+        state: experimental
+    BLE_LL_ISO_BROADCASTER:
+        description: >
+            Enable support for Isochronous Broadcasting state.
+        restrictions:
+            - BLE_LL_ISO if 1
+        value: 0
+        state: experimental
+
     BLE_LL_SYSINIT_STAGE:
         description: >
             Sysinit stage for the NimBLE controller.


Reply via email to