Bluetooth: controller: BIG create sends empty BIS PDUs (ULL)
When a BIG is create, it will now start sending empty BIS PDUs; at least from an ULL perspective, as the LLL support is still missing. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
parent
5a512fbe2f
commit
339b9b8df5
7 changed files with 273 additions and 2 deletions
|
@ -193,6 +193,23 @@ config BT_CTLR_TX_BUFFER_SIZE
|
|||
Maximum is set to 251 due to implementation limitations (use of
|
||||
uint8_t for length field in PDU buffer structure).
|
||||
|
||||
config BT_CTLR_ISO_TX_BUFFERS
|
||||
int "Number of Isochronous Tx buffers"
|
||||
default 3
|
||||
range 1 19
|
||||
depends on BT_CTLR_ADV_ISO || BT_CTLR_CENTRAL_ISO || BT_CTLR_PERIPHERAL_ISO
|
||||
help
|
||||
Set the number of Isochronous Tx PDUs to be queued for transmission
|
||||
in the controller.
|
||||
|
||||
config BT_CTLR_ISO_TX_BUFFER_SIZE
|
||||
int "Isochronous Tx buffer size"
|
||||
range 1 4095
|
||||
default 27
|
||||
help
|
||||
Size of the Isochronous Tx buffers and the value returned in HCI LE
|
||||
Read Buffer Size V2 command response.
|
||||
|
||||
choice BT_CTLR_TX_PWR
|
||||
prompt "Tx Power"
|
||||
default BT_CTLR_TX_PWR_0
|
||||
|
|
|
@ -58,6 +58,11 @@ enum {
|
|||
TICKER_ID_ADV_SYNC_BASE,
|
||||
TICKER_ID_ADV_SYNC_LAST = ((TICKER_ID_ADV_SYNC_BASE) +
|
||||
(CONFIG_BT_CTLR_ADV_SYNC_SET) - 1),
|
||||
#if defined(CONFIG_BT_CTLR_ADV_ISO)
|
||||
TICKER_ID_ADV_ISO_BASE,
|
||||
TICKER_ID_ADV_ISO_LAST = ((TICKER_ID_ADV_ISO_BASE) +
|
||||
(CONFIG_BT_CTLR_ADV_ISO_SET) - 1),
|
||||
#endif /* CONFIG_BT_CTLR_ADV_ISO */
|
||||
#endif /* CONFIG_BT_CTLR_ADV_PERIODIC */
|
||||
#endif /* CONFIG_BT_CTLR_ADV_AUX_SET > 0 */
|
||||
#endif /* CONFIG_BT_CTLR_ADV_EXT */
|
||||
|
|
|
@ -690,6 +690,17 @@ struct pdu_big_ctrl {
|
|||
} __packed;
|
||||
} __packed;
|
||||
|
||||
enum pdu_bis_llid {
|
||||
/** Unframed complete or end fragment */
|
||||
PDU_BIS_LLID_COMPLETE_END = 0x00,
|
||||
/** Unframed start or continuation fragment */
|
||||
PDU_BIS_LLID_START_CONTINUE = 0x01,
|
||||
/** Framed; one or more segments of a SDU */
|
||||
PDU_BIS_LLID_FRAMED = 0x02,
|
||||
/** BIG Control PDU */
|
||||
PDU_BIS_LLID_CTRL = 0x03,
|
||||
};
|
||||
|
||||
struct pdu_bis {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
uint8_t ll_id:2;
|
||||
|
|
|
@ -403,6 +403,13 @@ int ll_init(struct k_sem *sem_rx)
|
|||
}
|
||||
#endif /* CONFIG_BT_CONN */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_ADV_ISO)
|
||||
err = ull_adv_iso_init();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_BT_CONN */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_USER_EXT)
|
||||
err = ull_user_init();
|
||||
if (err) {
|
||||
|
@ -450,6 +457,12 @@ void ll_reset(void)
|
|||
LL_ASSERT(!err);
|
||||
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
|
||||
/* Reset periodic sync sets */
|
||||
err = ull_adv_iso_reset();
|
||||
LL_ASSERT(!err);
|
||||
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
|
||||
|
||||
#if defined(CONFIG_BT_CONN)
|
||||
#if defined(CONFIG_BT_CENTRAL)
|
||||
/* Reset initiator */
|
||||
|
|
|
@ -128,4 +128,7 @@ uint32_t ull_adv_sync_start(struct ll_adv_sync_set *sync,
|
|||
|
||||
/* helper function to schedule a mayfly to get sync offset */
|
||||
void ull_adv_sync_offset_get(struct ll_adv_set *adv);
|
||||
|
||||
int ull_adv_iso_init(void);
|
||||
int ull_adv_iso_reset(void);
|
||||
#endif /* CONFIG_BT_CTLR_ADV_EXT */
|
||||
|
|
|
@ -11,20 +11,131 @@
|
|||
#include "common/log.h"
|
||||
#include "hal/debug.h"
|
||||
#include "hal/cpu.h"
|
||||
#include "hal/ccm.h"
|
||||
#include "hal/ticker.h"
|
||||
|
||||
#include "util/util.h"
|
||||
#include "util/memq.h"
|
||||
#include "util/mayfly.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/mfifo.h"
|
||||
#include "ticker/ticker.h"
|
||||
|
||||
#include "pdu.h"
|
||||
#include "ll.h"
|
||||
#include "lll.h"
|
||||
|
||||
#include "lll_vendor.h"
|
||||
#include "lll_adv.h"
|
||||
#include "lll_scan.h"
|
||||
#include "lll_conn.h"
|
||||
|
||||
#include "ull_internal.h"
|
||||
#include "ull_adv_types.h"
|
||||
#include "ull_adv_internal.h"
|
||||
|
||||
static struct ll_adv_iso ll_adv_iso[CONFIG_BT_CTLR_ADV_SET];
|
||||
static void *adv_iso_free;
|
||||
|
||||
static MFIFO_DEFINE(iso_tx, sizeof(struct lll_tx),
|
||||
CONFIG_BT_CTLR_ISO_TX_BUFFERS);
|
||||
|
||||
/* TODO: Share between BIS and CIS */
|
||||
static struct {
|
||||
void *free;
|
||||
uint8_t pool[CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE *
|
||||
CONFIG_BT_CTLR_ISO_TX_BUFFERS];
|
||||
} mem_iso_tx;
|
||||
|
||||
static struct ll_adv_iso *adv_iso_by_bis_handle_get(uint16_t bis_handle)
|
||||
{
|
||||
struct ll_adv_iso *adv_iso;
|
||||
uint8_t idx;
|
||||
|
||||
adv_iso = &ll_adv_iso[0];
|
||||
|
||||
for (idx = 0U; idx < CONFIG_BT_CTLR_ADV_SET; idx++, adv_iso++) {
|
||||
if (adv_iso->is_created &&
|
||||
(adv_iso->bis_handle == bis_handle)) {
|
||||
return adv_iso;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ll_iso_tx_mem_acquire(void)
|
||||
{
|
||||
return mem_acquire(&mem_iso_tx.free);
|
||||
}
|
||||
|
||||
void ll_iso_tx_mem_release(void *tx)
|
||||
{
|
||||
mem_release(tx, &mem_iso_tx.free);
|
||||
}
|
||||
|
||||
static int init_reset(void)
|
||||
{
|
||||
/* Initialize conn pool. */
|
||||
mem_init(ll_adv_iso, sizeof(struct ll_adv_iso),
|
||||
sizeof(ll_adv_iso) / sizeof(struct ll_adv_iso), &adv_iso_free);
|
||||
|
||||
/* Initialize tx pool. */
|
||||
mem_init(mem_iso_tx.pool, CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE,
|
||||
CONFIG_BT_CTLR_ISO_TX_BUFFERS, &mem_iso_tx.free);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ull_adv_iso_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = init_reset();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ull_adv_iso_reset(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Re-initialize the Tx mfifo */
|
||||
MFIFO_INIT(iso_tx);
|
||||
|
||||
err = init_reset();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ll_iso_tx_mem_enqueue(uint16_t handle, void *tx)
|
||||
{
|
||||
struct lll_tx *lll_tx;
|
||||
struct ll_adv_iso *adv_iso;
|
||||
uint8_t idx;
|
||||
|
||||
adv_iso = adv_iso_by_bis_handle_get(handle);
|
||||
if (!adv_iso) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
idx = MFIFO_ENQUEUE_GET(iso_tx, (void **) &lll_tx);
|
||||
if (!lll_tx) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
lll_tx->handle = handle;
|
||||
lll_tx->node = tx;
|
||||
|
||||
MFIFO_ENQUEUE(iso_tx, idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ll_adv_iso_by_hci_handle_get(uint8_t hci_handle, uint8_t *handle)
|
||||
{
|
||||
|
@ -72,6 +183,93 @@ uint8_t ll_adv_iso_by_hci_handle_new(uint8_t hci_handle, uint8_t *handle)
|
|||
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
|
||||
}
|
||||
|
||||
|
||||
static void ticker_cb(uint32_t ticks_at_expire, uint32_t remainder,
|
||||
uint16_t lazy, void *param)
|
||||
{
|
||||
/* TODO: LLL support for ADV ISO */
|
||||
#if 0
|
||||
static memq_link_t link;
|
||||
static struct mayfly mfy = {0, 0, &link, NULL, lll_adv_iso_prepare};
|
||||
static struct lll_prepare_param p;
|
||||
struct ll_adv_iso *adv_iso = param;
|
||||
struct lll_adv_iso *lll;
|
||||
uint32_t ret;
|
||||
uint8_t ref;
|
||||
|
||||
DEBUG_RADIO_PREPARE_A(1);
|
||||
|
||||
lll = &adv_iso->lll;
|
||||
|
||||
/* Increment prepare reference count */
|
||||
ref = ull_ref_inc(&adv_iso->ull);
|
||||
LL_ASSERT(ref);
|
||||
|
||||
/* Append timing parameters */
|
||||
p.ticks_at_expire = ticks_at_expire;
|
||||
p.remainder = remainder;
|
||||
p.lazy = lazy;
|
||||
p.param = lll;
|
||||
mfy.param = &p;
|
||||
|
||||
/* Kick LLL prepare */
|
||||
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH,
|
||||
TICKER_USER_ID_LLL, 0, &mfy);
|
||||
LL_ASSERT(!ret);
|
||||
|
||||
DEBUG_RADIO_PREPARE_A(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t ull_adv_iso_start(struct ll_adv_iso *adv_iso, uint32_t ticks_anchor)
|
||||
{
|
||||
uint32_t ticks_slot_overhead;
|
||||
uint32_t volatile ret_cb;
|
||||
uint32_t iso_interval_us;
|
||||
uint32_t slot_us;
|
||||
uint32_t ret;
|
||||
|
||||
ull_hdr_init(&adv_iso->ull);
|
||||
|
||||
/* TODO: Calc slot_us */
|
||||
slot_us = EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US;
|
||||
slot_us += 1000;
|
||||
|
||||
adv_iso->evt.ticks_active_to_start = 0;
|
||||
adv_iso->evt.ticks_xtal_to_start =
|
||||
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US);
|
||||
adv_iso->evt.ticks_preempt_to_start =
|
||||
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US);
|
||||
adv_iso->evt.ticks_slot = HAL_TICKER_US_TO_TICKS(slot_us);
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
|
||||
ticks_slot_overhead = MAX(adv_iso->evt.ticks_active_to_start,
|
||||
adv_iso->evt.ticks_xtal_to_start);
|
||||
} else {
|
||||
ticks_slot_overhead = 0;
|
||||
}
|
||||
|
||||
/* TODO: Calculate ISO interval */
|
||||
/* iso_interval shall be at least SDU interval,
|
||||
* or integer multiple of SDU interval for unframed PDUs
|
||||
*/
|
||||
iso_interval_us = adv_iso->sdu_interval;
|
||||
|
||||
ret_cb = TICKER_STATUS_BUSY;
|
||||
ret = ticker_start(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_THREAD,
|
||||
(TICKER_ID_ADV_ISO_BASE + adv_iso->bis_handle),
|
||||
ticks_anchor, 0,
|
||||
HAL_TICKER_US_TO_TICKS(iso_interval_us),
|
||||
HAL_TICKER_REMAINDER(iso_interval_us),
|
||||
TICKER_NULL_LAZY,
|
||||
(ll_adv_iso->evt.ticks_slot + ticks_slot_overhead),
|
||||
ticker_cb, ll_adv_iso,
|
||||
ull_ticker_status_give, (void *)&ret_cb);
|
||||
ret = ull_ticker_status_take(ret, &ret_cb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct ll_adv_iso *ull_adv_iso_get(uint8_t handle)
|
||||
{
|
||||
if (handle >= CONFIG_BT_CTLR_ADV_SET) {
|
||||
|
@ -90,6 +288,8 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
|||
struct ll_adv_iso *adv_iso;
|
||||
struct ll_adv_set *adv;
|
||||
struct node_rx_pdu *node_rx;
|
||||
struct pdu_bis *pdu_bis;
|
||||
struct node_tx *tx;
|
||||
|
||||
adv_iso = ull_adv_iso_get(big_handle);
|
||||
|
||||
|
@ -170,6 +370,28 @@ uint8_t ll_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
|||
/* TODO: Add ACAD to AUX_SYNC_IND */
|
||||
|
||||
/* TODO: start sending BIS empty data packet for each BIS */
|
||||
ull_adv_iso_start(adv_iso, 0 /* TODO: Calc ticks_anchor */);
|
||||
|
||||
tx = ll_iso_tx_mem_acquire();
|
||||
if (!tx) {
|
||||
BT_ERR("Tx Buffer Overflow");
|
||||
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
|
||||
}
|
||||
|
||||
adv_iso->is_created = true;
|
||||
pdu_bis = (void *)tx->pdu;
|
||||
|
||||
/* Empty BIS packet */
|
||||
pdu_bis->length = 0;
|
||||
pdu_bis->ll_id = PDU_BIS_LLID_COMPLETE_END;
|
||||
pdu_bis->cssn = 0;
|
||||
pdu_bis->cstf = 0;
|
||||
|
||||
if (ll_iso_tx_mem_enqueue(adv_iso->bis_handle, tx)) {
|
||||
BT_ERR("Invalid Tx Enqueue");
|
||||
ll_iso_tx_mem_release(tx);
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
|
||||
/* Prepare BIG complete event */
|
||||
node_rx = (void *)&adv_iso->node_rx_complete;
|
||||
|
|
|
@ -63,7 +63,6 @@ struct ll_adv_sync_set {
|
|||
struct ll_adv_iso {
|
||||
struct evt_hdr evt;
|
||||
struct ull_hdr ull;
|
||||
struct ll_adv_aux_set adv_aux;
|
||||
struct lll_adv_iso lll;
|
||||
|
||||
uint8_t hci_handle;
|
||||
|
@ -86,6 +85,7 @@ struct ll_adv_iso {
|
|||
uint8_t bcode[16];
|
||||
|
||||
struct node_rx_hdr *node_rx_complete;
|
||||
struct pdu_bis pdu;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_BT_CTLR_ADV_EXT */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue