Bluetooth: controller: Enable back-to-back chaining for periodic adv

This enables chaining ota for periodic advertising. AUX_CHAIN_IND PDUs
will be sent automatically if AuxPtr is detected in preceding PDU.
AuxPtr offset is always set to achieve minimal required frame spacing,
i.e. 300us (T_mafs). AuxPtr in all PDUs in advertising train are
updated on enqueue since PDU spacing is already known at that time so
we do not need to waste time in LLL.

Signed-off-by: Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
This commit is contained in:
Andrzej Kaczmarek 2021-02-02 15:37:05 +01:00 committed by Anas Nashif
commit 7c81ed71d0
6 changed files with 197 additions and 12 deletions

View file

@ -172,6 +172,26 @@ config BT_CTLR_ADV_PDU_LINK
is required to enable advertising data trains (i.e. transmission of is required to enable advertising data trains (i.e. transmission of
AUX_CHAIN_IND). AUX_CHAIN_IND).
config BT_CTLR_ADV_SYNC_PDU_BACK2BACK
bool "Enable back-to-back transmission of periodic advertising trains"
depends on BT_CTLR_ADV_PERIODIC
select BT_CTLR_ADV_PDU_LINK
help
Enables transmission of AUX_CHAIN_IND in periodic advertising train by
sending each AUX_CHAIN_IND one after another back-to-back.
Note, consecutive AUX_CHAIN_IND packets are not scheduled but sent at
a constant offset on a best effort basis. This means advertising train can
be preempted by other event at any time.
config BT_CTLR_ADV_SYNC_PDU_BACK2BACK_AFS
int "AUX Frame Space for back-to-back transmission of periodic advertising trains"
depends on BT_CTLR_ADV_SYNC_PDU_BACK2BACK
default 300
range 300 1000
help
Specific AUX Frame Space to be used for back-to-back transmission of
periodic advertising trains. Time specified in microseconds.
config BT_CTLR_ADV_DATA_BUF_MAX config BT_CTLR_ADV_DATA_BUF_MAX
int "Advertising Data Maximum Buffers" int "Advertising Data Maximum Buffers"
depends on BT_BROADCASTER depends on BT_BROADCASTER

View file

@ -27,6 +27,9 @@ struct lll_adv_sync {
uint32_t ticks_offset; uint32_t ticks_offset;
struct lll_adv_pdu data; struct lll_adv_pdu data;
#if defined(CONFIG_BT_CTLR_ADV_PDU_LINK)
struct pdu_adv *last_pdu;
#endif /* CONFIG_BT_CTLR_ADV_PDU_LINK */
#if defined(CONFIG_BT_CTLR_ADV_ISO) #if defined(CONFIG_BT_CTLR_ADV_ISO)
struct lll_adv_iso *iso; struct lll_adv_iso *iso;

View file

@ -91,6 +91,10 @@ static inline struct pdu_adv *lll_adv_aux_data_curr_get(struct lll_adv_aux *lll)
#if defined(CONFIG_BT_CTLR_ADV_PERIODIC) #if defined(CONFIG_BT_CTLR_ADV_PERIODIC)
int lll_adv_and_extra_data_release(struct lll_adv_pdu *pdu); int lll_adv_and_extra_data_release(struct lll_adv_pdu *pdu);
#if defined(CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK)
void lll_adv_sync_pdu_b2b_update(struct lll_adv_sync *lll, uint8_t idx);
#endif
struct pdu_adv *lll_adv_pdu_and_extra_data_alloc(struct lll_adv_pdu *pdu, struct pdu_adv *lll_adv_pdu_and_extra_data_alloc(struct lll_adv_pdu *pdu,
void **extra_data, void **extra_data,
uint8_t *idx); uint8_t *idx);

View file

@ -33,6 +33,7 @@
#include "lll_internal.h" #include "lll_internal.h"
#include "lll_adv_internal.h" #include "lll_adv_internal.h"
#include "lll_tim_internal.h" #include "lll_tim_internal.h"
#include "lll_prof_internal.h"
#include "lll_df_internal.h" #include "lll_df_internal.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
@ -40,10 +41,21 @@
#include "common/log.h" #include "common/log.h"
#include "hal/debug.h" #include "hal/debug.h"
#if defined(CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK)
#define ADV_SYNC_PDU_B2B_AFS (CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK_AFS)
#endif
static int init_reset(void); static int init_reset(void);
static int prepare_cb(struct lll_prepare_param *p); static int prepare_cb(struct lll_prepare_param *p);
static void abort_cb(struct lll_prepare_param *prepare_param, void *param); static void abort_cb(struct lll_prepare_param *prepare_param, void *param);
static void isr_done(void *param); static void isr_done(void *param);
#if defined(CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK)
static void isr_tx(void *param);
static void pdu_b2b_update(struct lll_adv_sync *lll, struct pdu_adv *pdu);
static void pdu_b2b_aux_ptr_update(struct pdu_adv *pdu, uint8_t phy,
uint8_t flags, uint8_t chan_idx,
uint32_t tifs);
#endif /* CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK */
int lll_adv_sync_init(void) int lll_adv_sync_init(void)
{ {
@ -148,6 +160,15 @@ static int prepare_cb(struct lll_prepare_param *p)
pdu = lll_adv_sync_data_latest_get(lll, &extra_data, &upd); pdu = lll_adv_sync_data_latest_get(lll, &extra_data, &upd);
LL_ASSERT(pdu); LL_ASSERT(pdu);
#if defined(CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK)
if (upd) {
/* AuxPtr offsets for b2b TX are fixed for given chain so we can
* calculate them here in advance.
*/
pdu_b2b_update(lll, pdu);
}
#endif
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX) #if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
if (extra_data) { if (extra_data) {
df_cfg = (struct lll_df_adv_cfg *)extra_data; df_cfg = (struct lll_df_adv_cfg *)extra_data;
@ -162,9 +183,18 @@ static int prepare_cb(struct lll_prepare_param *p)
radio_pkt_tx_set(pdu); radio_pkt_tx_set(pdu);
/* TODO: chaining */ #if defined(CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK)
radio_isr_set(isr_done, lll); if (pdu->adv_ext_ind.ext_hdr_len && pdu->adv_ext_ind.ext_hdr.aux_ptr) {
lll->last_pdu = pdu;
radio_isr_set(isr_tx, lll);
radio_tmr_tifs_set(ADV_SYNC_PDU_B2B_AFS);
radio_switch_complete_and_b2b_tx(phy_s, 0, phy_s, 0);
#else
if (0) {
#endif /* CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK */
} else {
radio_isr_set(isr_done, lll);
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX) #if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
if (df_cfg) { if (df_cfg) {
radio_switch_complete_and_phy_end_disable(); radio_switch_complete_and_phy_end_disable();
@ -173,6 +203,7 @@ static int prepare_cb(struct lll_prepare_param *p)
{ {
radio_switch_complete_and_disable(); radio_switch_complete_and_disable();
} }
}
ticks_at_event = p->ticks_at_expire; ticks_at_event = p->ticks_at_expire;
ull = HDR_LLL2ULL(lll); ull = HDR_LLL2ULL(lll);
@ -256,3 +287,132 @@ static void isr_done(void *param)
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */ #endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
lll_isr_done(lll); lll_isr_done(lll);
} }
#if defined(CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK)
static void isr_tx(void *param)
{
struct lll_adv_sync *lll_sync;
struct pdu_adv *pdu;
struct lll_adv *lll;
if (IS_ENABLED(CONFIG_BT_CTLR_PROFILE_ISR)) {
lll_prof_latency_capture();
}
/* Clear radio tx status and events */
lll_isr_tx_status_reset();
lll_sync = param;
lll = lll_sync->adv;
/* TODO: do not hardcode to single value */
lll_chan_set(0);
pdu = lll_adv_pdu_linked_next_get(lll_sync->last_pdu);
LL_ASSERT(pdu);
lll_sync->last_pdu = pdu;
/* setup tIFS switching */
if (pdu->adv_ext_ind.ext_hdr_len && pdu->adv_ext_ind.ext_hdr.aux_ptr) {
radio_tmr_tifs_set(ADV_SYNC_PDU_B2B_AFS);
radio_isr_set(isr_tx, lll_sync);
radio_switch_complete_and_b2b_tx(lll->phy_s, 0, lll->phy_s, 0);
} else {
radio_isr_set(lll_isr_done, lll);
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
if (lll_sync->cte_started) {
radio_switch_complete_and_phy_end_disable();
} else
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
{
radio_switch_complete_and_disable();
}
}
radio_pkt_tx_set(pdu);
/* assert if radio packet ptr is not set and radio started rx */
LL_ASSERT(!radio_is_ready());
if (IS_ENABLED(CONFIG_BT_CTLR_PROFILE_ISR)) {
lll_prof_cputime_capture();
}
/* capture end of AUX_SYNC_IND/AUX_CHAIN_IND PDU, used for calculating
* next PDU timestamp.
*/
radio_tmr_end_capture();
#if defined(CONFIG_BT_CTLR_GPIO_LNA_PIN)
if (IS_ENABLED(CONFIG_BT_CTLR_PROFILE_ISR)) {
/* PA/LNA enable is overwriting packet end used in ISR
* profiling, hence back it up for later use.
*/
lll_prof_radio_end_backup();
}
radio_gpio_lna_setup();
radio_gpio_pa_lna_enable(radio_tmr_tifs_base_get() +
ADV_SYNC_PDU_B2B_AFS - 4 -
radio_tx_chain_delay_get(lll->phy_s, 0) -
CONFIG_BT_CTLR_GPIO_LNA_OFFSET);
#endif /* CONFIG_BT_CTLR_GPIO_LNA_PIN */
if (IS_ENABLED(CONFIG_BT_CTLR_PROFILE_ISR)) {
lll_prof_send();
}
}
static void pdu_b2b_update(struct lll_adv_sync *lll, struct pdu_adv *pdu)
{
while (pdu) {
pdu_b2b_aux_ptr_update(pdu, lll->adv->phy_s, 0, 0,
ADV_SYNC_PDU_B2B_AFS);
pdu = lll_adv_pdu_linked_next_get(pdu);
}
}
static void pdu_b2b_aux_ptr_update(struct pdu_adv *pdu, uint8_t phy,
uint8_t flags, uint8_t chan_idx,
uint32_t tifs)
{
struct pdu_adv_com_ext_adv *com_hdr;
struct pdu_adv_ext_hdr *hdr;
struct pdu_adv_aux_ptr *aux;
uint32_t offs;
uint8_t *dptr;
com_hdr = &pdu->adv_ext_ind;
hdr = &com_hdr->ext_hdr;
/* Skip flags */
dptr = hdr->data;
if (!com_hdr->ext_hdr_len || !hdr->aux_ptr) {
return;
}
LL_ASSERT(!hdr->adv_addr);
LL_ASSERT(!hdr->tgt_addr);
if (hdr->cte_info) {
dptr++;
}
LL_ASSERT(!hdr->adi);
/* Update AuxPtr */
aux = (void *)dptr;
offs = PKT_AC_US(pdu->len, phy) + tifs;
offs = offs / OFFS_UNIT_30_US;
if ((offs >> 13) != 0) {
aux->offs = offs / (OFFS_UNIT_300_US / OFFS_UNIT_30_US);
aux->offs_units = 1U;
} else {
aux->offs = offs;
aux->offs_units = 0U;
}
aux->chan_idx = chan_idx;
aux->ca = 0;
aux->phy = find_lsb_set(phy) - 1;
}
#endif /* CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK */

View file

@ -146,7 +146,7 @@ void lll_scan_prepare_connect_req(struct lll_scan *lll, struct pdu_adv *pdu_tx,
conn_interval_us = (uint32_t)lll_conn->interval * CONN_INT_UNIT_US; conn_interval_us = (uint32_t)lll_conn->interval * CONN_INT_UNIT_US;
conn_offset_us = radio_tmr_end_get() + EVENT_IFS_US + conn_offset_us = radio_tmr_end_get() + EVENT_IFS_US +
PKT_AC_US(sizeof(struct pdu_adv_connect_ind), 0, PKT_AC_US(sizeof(struct pdu_adv_connect_ind),
phy == PHY_LEGACY ? PHY_1M : phy); phy == PHY_LEGACY ? PHY_1M : phy);
/* Add transmitWindowDelay to default calculated connection offset: /* Add transmitWindowDelay to default calculated connection offset:

View file

@ -434,11 +434,9 @@ static int isr_rx_pdu(struct lll_scan_aux *lll, uint8_t devmatch_ok,
ull = HDR_LLL2ULL(lll_scan); ull = HDR_LLL2ULL(lll_scan);
if (pdu_end_us > (HAL_TICKER_TICKS_TO_US(ull->ticks_slot) - if (pdu_end_us > (HAL_TICKER_TICKS_TO_US(ull->ticks_slot) -
EVENT_IFS_US - EVENT_IFS_US -
PKT_AC_US(aux_connect_req_len, 0, PKT_AC_US(aux_connect_req_len, lll->phy) -
lll->phy) -
EVENT_IFS_US - EVENT_IFS_US -
PKT_AC_US(aux_connect_rsp_len, 0, PKT_AC_US(aux_connect_rsp_len, lll->phy) -
lll->phy) -
EVENT_OVERHEAD_START_US - EVENT_OVERHEAD_START_US -
EVENT_TICKER_RES_MARGIN_US)) { EVENT_TICKER_RES_MARGIN_US)) {
return -ETIME; return -ETIME;