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:
parent
5b62fae902
commit
20939b7b11
8 changed files with 325 additions and 60 deletions
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue