Bluetooth: controller: Periodic Advertising Report generation

Added implementation for LE Periodic Advertising Report
event generation.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
This commit is contained in:
Vinayak Kariappa Chettimada 2020-11-05 18:13:25 +05:30 committed by Carles Cufí
commit 20939b7b11
8 changed files with 325 additions and 60 deletions

View file

@ -4429,6 +4429,205 @@ static void le_per_adv_sync_established(struct pdu_data *pdu_data,
sep->clock_accuracy = se->sca;
}
static void le_per_adv_sync_report(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_per_advertising_report *sep;
struct pdu_adv *adv = (void *)pdu_data;
struct node_rx_pdu *node_rx_curr;
struct node_rx_pdu *node_rx_next;
uint8_t total_data_len = 0U;
uint8_t data_status = 0U;
uint8_t data_len = 0U;
int8_t tx_pwr = 0x7f;
uint8_t *data = NULL;
int8_t rssi;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_PER_ADVERTISING_REPORT)) {
return;
}
node_rx_curr = node_rx;
node_rx_next = node_rx_curr->hdr.rx_ftr.extra;
do {
struct pdu_adv_com_ext_adv *p;
uint8_t data_len_curr = 0U;
uint8_t *data_curr = NULL;
uint8_t sec_phy_curr = 0U;
struct pdu_adv_hdr *h;
uint8_t hdr_len;
uint8_t *ptr;
/* The Link Layer currently returns RSSI as an absolute value */
rssi = -(node_rx_curr->hdr.rx_ftr.rssi);
BT_DBG("len = %u, rssi = %d", adv->len, rssi);
p = (void *)&adv->adv_ext_ind;
h = (void *)p->ext_hdr_adi_adv_data;
ptr = (void *)h;
BT_DBG(" Ext. adv mode= 0x%x, hdr len= %u", p->adv_mode,
p->ext_hdr_len);
if (!p->ext_hdr_len) {
hdr_len = ptr - (uint8_t *)p;
goto no_ext_hdr;
}
ptr += sizeof(*h);
/* No AdvA */
/* No TargetA */
/* No ADI */
/* AuxPtr */
if (h->aux_ptr) {
struct pdu_adv_aux_ptr *aux;
uint8_t aux_phy;
aux = (void *)ptr;
ptr += sizeof(*aux);
sec_phy_curr = aux->phy + 1;
aux_phy = BIT(aux->phy);
BT_DBG(" AuxPtr chan_idx = %u, ca = %u, offs_units "
"= %u offs = 0x%x, phy = 0x%x", aux->chan_idx,
aux->ca, aux->offs_units, aux->offs, aux_phy);
}
/* No SyncInfo */
/* Tx Power */
if (h->tx_pwr) {
tx_pwr = *(int8_t *)ptr;
ptr++;
BT_DBG(" Tx pwr= %d dB", tx_pwr);
}
hdr_len = ptr - (uint8_t *)p;
if (hdr_len <= (offsetof(struct pdu_adv_com_ext_adv,
ext_hdr_adi_adv_data) +
sizeof(struct pdu_adv_hdr))) {
hdr_len = offsetof(struct pdu_adv_com_ext_adv,
ext_hdr_adi_adv_data);
ptr = (uint8_t *)h;
}
if (hdr_len > (p->ext_hdr_len +
offsetof(struct pdu_adv_com_ext_adv,
ext_hdr_adi_adv_data))) {
BT_WARN(" Header length %u/%u, INVALID.", hdr_len,
p->ext_hdr_len);
} else {
uint8_t acad_len = p->ext_hdr_len +
offsetof(struct pdu_adv_com_ext_adv,
ext_hdr_adi_adv_data) -
hdr_len;
if (acad_len) {
ptr += acad_len;
hdr_len += acad_len;
BT_DBG("ACAD: <todo>");
}
}
no_ext_hdr:
if (hdr_len < adv->len) {
data_len_curr = adv->len - hdr_len;
data_curr = ptr;
BT_DBG(" AD Data (%u): <todo>", data_len);
}
if (node_rx_curr == node_rx) {
data_len = data_len_curr;
total_data_len = data_len;
data = data_curr;
} else {
/* TODO: Validate current value with previous ??
*/
if (!data) {
data_len = data_len_curr;
total_data_len = data_len;
data = data_curr;
} else {
total_data_len += data_len_curr;
/* TODO: construct new HCI event for this
* fragment.
*/
}
}
if (!node_rx_next) {
if (sec_phy_curr) {
data_status = BIT(1);
}
break;
}
node_rx_curr = node_rx_next;
node_rx_next = node_rx_curr->hdr.rx_ftr.extra;
adv = (void *)node_rx_curr->pdu;
} while (1);
/* FIXME: move most of below into above loop to dispatch fragments of
* data in HCI event.
*/
/* If data complete */
if (!data_status) {
uint8_t data_max_len;
data_max_len = CONFIG_BT_DISCARDABLE_BUF_SIZE -
BT_HCI_ACL_HDR_SIZE - sizeof(*sep);
/* if data cannot fit the event, mark it as incomplete */
if (data_len > data_max_len) {
data_len = data_max_len;
data_status = BIT(0);
}
} else {
/* Data incomplete and no more to come */
if ((tx_pwr == 0x7f) && !data) {
goto le_per_adv_report_invalid;
}
}
/* Start constructing the event */
sep = meta_evt(buf, BT_HCI_EVT_LE_PER_ADVERTISING_REPORT,
sizeof(*sep) + data_len);
sep->handle = sys_cpu_to_le16(node_rx->hdr.handle);
sep->tx_power = tx_pwr;
sep->rssi = rssi;
sep->cte_type = 0U; /* TODO */
sep->data_status = data_status;
sep->length = data_len;
memcpy(&sep->data[0], data, data_len);
le_per_adv_report_invalid:
/* Free the node_rx list */
node_rx_next = node_rx->hdr.rx_ftr.extra;
while (node_rx_next) {
node_rx_curr = node_rx_next;
node_rx_next = node_rx_curr->hdr.rx_ftr.extra;
node_rx_curr->hdr.next = NULL;
ll_rx_mem_release((void **)&node_rx_curr);
}
}
static void le_per_adv_sync_lost(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
@ -4807,6 +5006,9 @@ static void encode_control(struct node_rx_pdu *node_rx,
case NODE_RX_TYPE_SYNC:
le_per_adv_sync_established(pdu_data, node_rx, buf);
break;
case NODE_RX_TYPE_SYNC_REPORT:
le_per_adv_sync_report(pdu_data, node_rx, buf);
break;
case NODE_RX_TYPE_SYNC_LOST:
le_per_adv_sync_lost(pdu_data, node_rx, buf);
break;
@ -5272,6 +5474,7 @@ uint8_t hci_get_class(struct node_rx_pdu *node_rx)
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
__fallthrough;
case NODE_RX_TYPE_SYNC:
case NODE_RX_TYPE_SYNC_REPORT:
case NODE_RX_TYPE_SYNC_LOST:
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
#endif /* CONFIG_BT_OBSERVER */

View file

@ -168,8 +168,10 @@ enum node_rx_type {
NODE_RX_TYPE_EXT_1M_REPORT,
NODE_RX_TYPE_EXT_2M_REPORT,
NODE_RX_TYPE_EXT_CODED_REPORT,
NODE_RX_TYPE_EXT_AUX_REPORT,
NODE_RX_TYPE_EXT_SCAN_TERMINATE,
NODE_RX_TYPE_SYNC,
NODE_RX_TYPE_SYNC_REPORT,
NODE_RX_TYPE_SYNC_LOST,
NODE_RX_TYPE_EXT_ADV_TERMINATE,
NODE_RX_TYPE_SCAN_REQ,

View file

@ -303,23 +303,7 @@ static int isr_rx_pdu(struct lll_scan_aux *lll, uint8_t rssi_ready)
trx_cnt++;
switch (lll->phy) {
case BIT(0):
node_rx->hdr.type = NODE_RX_TYPE_EXT_1M_REPORT;
break;
case BIT(1):
node_rx->hdr.type = NODE_RX_TYPE_EXT_2M_REPORT;
break;
case BIT(2):
node_rx->hdr.type = NODE_RX_TYPE_EXT_CODED_REPORT;
break;
default:
LL_ASSERT(0);
break;
}
node_rx->hdr.type = NODE_RX_TYPE_EXT_AUX_REPORT;
ftr = &(node_rx->hdr.rx_ftr);
ftr->param = lll;

View file

@ -273,9 +273,28 @@ static void isr_rx(void *param)
/* Check CRC and generate Periodic Advertising Report */
if (crc_ok) {
/* TODO: */
BT_INFO("CRC OK, Periodic event sync-ed, report generation"
" is unsupported, work in progress.");
struct node_rx_pdu *node_rx;
node_rx = ull_pdu_rx_alloc_peek(3);
if (node_rx) {
struct node_rx_ftr *ftr;
ull_pdu_rx_alloc();
node_rx->hdr.type = NODE_RX_TYPE_SYNC_REPORT;
ftr = &(node_rx->hdr.rx_ftr);
ftr->param = lll;
ftr->rssi = (rssi_ready) ? (radio_rssi_get() & 0x7f) :
0x7f;
ftr->ticks_anchor = radio_tmr_start_get();
ftr->radio_end_us = radio_tmr_end_get() -
radio_rx_chain_delay_get(lll->phy,
1);
ull_rx_put(node_rx->hdr.link, node_rx);
ull_rx_sched();
}
}
isr_rx_done:

View file

@ -634,6 +634,9 @@ void ll_rx_dequeue(void)
case NODE_RX_TYPE_EXT_1M_REPORT:
case NODE_RX_TYPE_EXT_2M_REPORT:
case NODE_RX_TYPE_EXT_CODED_REPORT:
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
case NODE_RX_TYPE_SYNC_REPORT:
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
{
struct node_rx_hdr *rx_curr;
struct pdu_adv *adv;
@ -967,6 +970,9 @@ void ll_rx_mem_release(void **node_rx)
case NODE_RX_TYPE_EXT_1M_REPORT:
case NODE_RX_TYPE_EXT_2M_REPORT:
case NODE_RX_TYPE_EXT_CODED_REPORT:
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
case NODE_RX_TYPE_SYNC_REPORT:
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#endif /* CONFIG_BT_OBSERVER */
@ -1861,11 +1867,13 @@ static inline int rx_demux_rx(memq_link_t *link, struct node_rx_hdr *rx)
#if defined(CONFIG_BT_OBSERVER)
#if defined(CONFIG_BT_CTLR_ADV_EXT)
case NODE_RX_TYPE_EXT_1M_REPORT:
case NODE_RX_TYPE_EXT_2M_REPORT:
case NODE_RX_TYPE_EXT_CODED_REPORT:
case NODE_RX_TYPE_EXT_AUX_REPORT:
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
case NODE_RX_TYPE_SYNC_REPORT:
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
{
struct pdu_adv *adv;
uint8_t phy = 0U;
memq_dequeue(memq_ull_rx.tail, &memq_ull_rx.head, NULL);
@ -1876,22 +1884,7 @@ static inline int rx_demux_rx(memq_link_t *link, struct node_rx_hdr *rx)
break;
}
switch (rx->type) {
case NODE_RX_TYPE_EXT_1M_REPORT:
phy = BIT(0);
break;
case NODE_RX_TYPE_EXT_2M_REPORT:
phy = BIT(1);
break;
case NODE_RX_TYPE_EXT_CODED_REPORT:
phy = BIT(2);
break;
default:
LL_ASSERT(0);
break;
}
ull_scan_aux_setup(link, rx, phy);
ull_scan_aux_setup(link, rx);
}
break;
#endif /* CONFIG_BT_CTLR_ADV_EXT */

View file

@ -565,6 +565,18 @@ uint8_t ull_scan_lll_handle_get(struct lll_scan *lll)
return ull_scan_handle_get((void *)lll->hdr.parent);
}
struct ll_scan_set *ull_scan_is_valid_get(struct ll_scan_set *scan)
{
if (((uint8_t *)scan < (uint8_t *)ll_scan) ||
((uint8_t *)scan > ((uint8_t *)ll_scan +
(sizeof(struct ll_scan_set) *
(BT_CTLR_SCAN_SET - 1))))) {
return NULL;
}
return scan;
}
struct ll_scan_set *ull_scan_is_enabled_get(uint8_t handle)
{
struct ll_scan_set *scan;

View file

@ -73,7 +73,7 @@ int ull_scan_aux_reset(void)
return 0;
}
void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx, uint8_t phy)
void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx)
{
struct pdu_adv_aux_ptr *aux_ptr;
struct pdu_adv_com_ext_adv *p;
@ -93,32 +93,81 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx, uint8_t phy)
struct pdu_adv *pdu;
uint8_t aux_handle;
uint8_t *ptr;
uint8_t phy;
ftr = &rx->rx_ftr;
switch (rx->type) {
case NODE_RX_TYPE_EXT_1M_REPORT:
lll = NULL;
aux = NULL;
scan = (void *)HDR_LLL2EVT(ftr->param);
sync = scan->per_scan.sync;
phy = BIT(0);
break;
case NODE_RX_TYPE_EXT_CODED_REPORT:
lll = NULL;
aux = NULL;
scan = (void *)HDR_LLL2EVT(ftr->param);
sync = scan->per_scan.sync;
phy = BIT(2);
break;
case NODE_RX_TYPE_EXT_AUX_REPORT:
lll = ftr->param;
aux = (void *)HDR_LLL2EVT(lll);
if (((uint8_t *)aux < (uint8_t *)ll_scan_aux_pool) ||
((uint8_t *)aux > ((uint8_t *)ll_scan_aux_pool +
(sizeof(struct ll_scan_aux_set) *
(CONFIG_BT_CTLR_SCAN_AUX_SET - 1))))) {
/* NOTE: We are most probably ADV_EXT_IND on primary channel */
aux = NULL;
scan = (void *)HDR_LLL2EVT(aux->rx_head->rx_ftr.param);
sync = (void *)scan;
scan = ull_scan_is_valid_get(scan);
if (scan) {
sync = scan->per_scan.sync;
phy = lll->phy;
switch (phy) {
case BIT(0):
rx->type = NODE_RX_TYPE_EXT_1M_REPORT;
break;
case BIT(1):
rx->type = NODE_RX_TYPE_EXT_2M_REPORT;
break;
case BIT(2):
rx->type = NODE_RX_TYPE_EXT_CODED_REPORT;
break;
default:
LL_ASSERT(0);
return;
}
} else {
rx->type = NODE_RX_TYPE_SYNC_REPORT;
rx->handle = ull_sync_handle_get(sync);
sync = NULL;
phy = lll->phy;
}
break;
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
case NODE_RX_TYPE_SYNC_REPORT:
{
struct ll_sync_set *ull_sync;
struct lll_sync *lll_sync;
/* set the sync handle corresponding to the LLL context
* passed in the node rx footer field.
*/
lll_sync = ftr->param;
ull_sync = (void *)HDR_LLL2EVT(lll_sync);
rx->handle = ull_sync_handle_get(ull_sync);
lll = NULL;
aux = NULL;
scan = NULL;
sync = NULL;
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
if (aux) {
struct lll_scan *lll_scan;
lll_scan = aux->rx_head->rx_ftr.param;
scan = (void *)HDR_LLL2EVT(lll_scan);
} else {
scan = (void *)HDR_LLL2EVT(ftr->param);
phy = lll_sync->phy;
}
sync = scan->per_scan.sync;
break;
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
default:
LL_ASSERT(0);
return;
}
rx->link = link;
ftr->extra = NULL;

View file

@ -50,6 +50,9 @@ struct ll_scan_set *ull_scan_set_get(uint8_t handle);
/* Return the scan set handle given the scan set instance */
uint8_t ull_scan_handle_get(struct ll_scan_set *scan);
/* Helper function to check and return if a valid scan context */
struct ll_scan_set *ull_scan_is_valid_get(struct ll_scan_set *scan);
/* Return ll_scan_set context if enabled */
struct ll_scan_set *ull_scan_is_enabled_get(uint8_t handle);
@ -66,7 +69,7 @@ int ull_scan_aux_init(void);
int ull_scan_aux_reset(void);
/* Helper to setup scanning on auxiliary channel */
void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx, uint8_t phy);
void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx);
/* Helper function to handle scan done events */
void ull_scan_done(struct node_rx_event_done *done);