Bluetooth: Controller: Fix Periodic Adv Report to scan max data length

Fix implementation to limit Periodic Advertising data to a
configurable maximum length when generating HCI reports.

Bluetooth Test Specification defines Scan_Max_Data value
in IXIT. When HCI LE Periodic Advertising Report events are
generated by assembling the chain PDUs, the test cases
expect that if the data length is no more than
Scan_Max_Data, then at least once the IUT shall not
truncate the data in the advertising report.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
This commit is contained in:
Vinayak Kariappa Chettimada 2021-12-01 21:01:23 +05:30 committed by Anas Nashif
parent 93d6d1e114
commit faa89c779c
6 changed files with 113 additions and 36 deletions

View File

@ -6366,7 +6366,7 @@ static void le_per_adv_sync_report(struct pdu_data *pdu_data,
uint8_t cte_type = BT_HCI_LE_NO_CTE;
struct pdu_adv_com_ext_adv *p;
struct pdu_adv_ext_hdr *h;
uint8_t data_status = 0U;
uint16_t data_len_total;
struct net_buf *evt_buf;
uint8_t data_len = 0U;
uint8_t acad_len = 0U;
@ -6533,13 +6533,20 @@ no_ext_hdr:
data_len_max = ADV_REPORT_EVT_MAX_LEN -
sizeof(struct bt_hci_evt_le_meta_event) -
sizeof(struct bt_hci_evt_le_per_advertising_report);
data_len_total = node_rx->hdr.rx_ftr.aux_data_len;
evt_buf = buf;
if ((le_event_mask & BT_EVT_MASK_LE_PER_ADVERTISING_REPORT) && accept) {
if ((le_event_mask & BT_EVT_MASK_LE_PER_ADVERTISING_REPORT) && accept &&
((data_len_total - data_len) < CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX)) {
data_len = MIN(data_len, (CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX +
data_len - data_len_total));
do {
struct bt_hci_evt_le_per_advertising_report *sep;
uint8_t data_len_frag;
uint8_t data_status;
data_len_frag = MIN(data_len, data_len_max);
@ -6548,17 +6555,30 @@ no_ext_hdr:
BT_HCI_EVT_LE_PER_ADVERTISING_REPORT,
sizeof(*sep) + data_len_frag);
sep->handle = sys_cpu_to_le16(node_rx->hdr.handle);
sep->tx_power = tx_pwr;
sep->rssi = rssi;
sep->cte_type = cte_type;
sep->length = data_len_frag;
memcpy(&sep->data[0], data, data_len_frag);
data += data_len_frag;
data_len -= data_len_frag;
if (data_len > 0) {
/* Some data left in PDU, mark as partial data. */
data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL;
} else if (!aux_ptr) {
evt_buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER);
net_buf_frag_add(buf, evt_buf);
tx_pwr = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
} else if (!aux_ptr &&
(data_len_total <= CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX)) {
/* No data left, no AuxPtr, mark as complete data. */
data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE;
} else if (ftr->aux_w4next) {
} else if (ftr->aux_sched &&
(data_len_total < CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX)) {
/* No data left, but have AuxPtr and scheduled aux scan,
* mark as partial data.
*/
@ -6570,22 +6590,7 @@ no_ext_hdr:
data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE;
}
sep->handle = sys_cpu_to_le16(node_rx->hdr.handle);
/* TODO: use actual TX power only on 1st report, subsequent
* reports can use 0x7F
*/
sep->tx_power = tx_pwr;
sep->rssi = rssi;
sep->cte_type = cte_type;
sep->data_status = data_status;
sep->length = data_len_frag;
if (data_len > 0) {
evt_buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER);
net_buf_frag_add(buf, evt_buf);
tx_pwr = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
}
} while (data_len > 0);
evt_buf = NULL;

View File

@ -327,21 +327,24 @@ struct node_rx_ftr {
#endif /* CONFIG_BT_CTLR_EXT_SCAN_FP */
#if defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_OBSERVER)
uint8_t phy_flags:1;
uint8_t scan_req:1;
uint8_t scan_rsp:1;
#if defined(CONFIG_BT_CTLR_PRIVACY)
uint8_t direct_resolved:1;
#endif /* CONFIG_BT_CTLR_PRIVACY */
uint8_t aux_lll_sched:1;
uint8_t aux_w4next:1;
uint8_t aux_failed:1;
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
uint8_t sync_status:2;
uint8_t sync_rx_enabled:1;
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
uint8_t phy_flags:1;
uint8_t scan_req:1;
uint8_t scan_rsp:1;
uint8_t aux_sched:1;
uint8_t aux_lll_sched:1;
uint8_t aux_failed:1;
uint16_t aux_data_len;
#endif /* CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_HCI_MESH_EXT)

View File

@ -107,9 +107,11 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx)
uint32_t ticker_status;
struct lll_scan *lll;
struct pdu_adv *pdu;
uint8_t hdr_buf_len;
uint8_t aux_handle;
bool is_scan_req;
uint8_t acad_len;
uint8_t data_len;
uint8_t hdr_len;
uint8_t *ptr;
uint8_t phy;
@ -273,11 +275,25 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx)
rx->link = link;
ftr->extra = NULL;
ftr->aux_w4next = 0;
ftr->aux_sched = 0U;
pdu = (void *)((struct node_rx_pdu *)rx)->pdu;
p = (void *)&pdu->adv_ext_ind;
if (!p->ext_hdr_len) {
data_len = pdu->len - PDU_AC_EXT_HEADER_SIZE_MIN;
if (sync_lll) {
struct ll_sync_set *sync;
sync = HDR_LLL2ULL(sync_lll);
ftr->aux_data_len = sync->data_len + data_len;
sync->data_len = 0U;
} else if (aux) {
aux->data_len += data_len;
ftr->aux_data_len = aux->data_len;
} else {
ftr->aux_data_len = data_len;
}
goto ull_scan_aux_rx_flush;
}
@ -355,14 +371,20 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx)
/* Calculate ACAD Len */
hdr_len = ptr - (uint8_t *)p;
if (hdr_len <= (p->ext_hdr_len + offsetof(struct pdu_adv_com_ext_adv,
ext_hdr_adv_data))) {
acad_len = p->ext_hdr_len +
offsetof(struct pdu_adv_com_ext_adv,
ext_hdr_adv_data) -
hdr_len;
} else {
hdr_buf_len = PDU_AC_EXT_HEADER_SIZE_MIN + p->ext_hdr_len;
if (hdr_len > hdr_buf_len) {
/* FIXME: Handle invalid header length */
acad_len = 0U;
} else {
acad_len = hdr_buf_len - hdr_len;
hdr_len += acad_len;
}
/* calculate total data length */
if (hdr_len < pdu->len) {
data_len = pdu->len - hdr_len;
} else {
data_len = 0U;
}
/* Periodic Advertising Channel Map Indication and/or Broadcast ISO
@ -386,6 +408,19 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx)
*/
if (!aux_ptr || !aux_ptr->offs || is_scan_req ||
(aux_ptr->phy > EXT_ADV_AUX_PHY_LE_CODED)) {
if (sync_lll) {
struct ll_sync_set *sync;
sync = HDR_LLL2ULL(sync_lll);
ftr->aux_data_len = sync->data_len + data_len;
sync->data_len = 0U;
} else if (aux) {
aux->data_len += data_len;
ftr->aux_data_len = aux->data_len;
} else {
ftr->aux_data_len = data_len;
}
if (is_scan_req) {
LL_ASSERT(aux && aux->rx_last);
@ -401,10 +436,19 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx)
if (!aux) {
aux = aux_acquire();
if (!aux) {
if (sync_lll) {
struct ll_sync_set *sync;
sync = HDR_LLL2ULL(sync_lll);
ftr->aux_data_len = sync->data_len + data_len;
sync->data_len = 0U;
}
goto ull_scan_aux_rx_flush;
}
aux->rx_head = aux->rx_last = NULL;
aux->data_len = data_len;
lll_aux = &aux->lll;
lll_aux->is_chain_sched = 0U;
@ -412,14 +456,25 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx)
lll_hdr_init(lll_aux, aux);
aux->parent = lll ? (void *)lll : (void *)sync_lll;
} else {
aux->data_len += data_len;
}
/* AUX_ADV_IND/AUX_CHAIN_IND PDU reception is being setup */
ftr->aux_sched = 1U;
/* In sync context we can dispatch rx immediately, in scan context we
* enqueue rx in aux context and will flush them after scan is complete.
*/
if (0) {
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
} else if (sync_lll) {
struct ll_sync_set *sync;
sync = HDR_LLL2ULL(sync_lll);
sync->data_len += data_len;
ftr->aux_data_len = sync->data_len;
ll_rx_put(link, rx);
ll_rx_sched();
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
@ -430,13 +485,15 @@ void ull_scan_aux_setup(memq_link_t *link, struct node_rx_hdr *rx)
aux->rx_head = rx;
}
aux->rx_last = rx;
ftr->aux_data_len = aux->data_len;
}
/* Initialize the channel index and PHY for the Auxiliary PDU reception.
*/
lll_aux->chan = aux_ptr->chan_idx;
lll_aux->phy = BIT(aux_ptr->phy);
ftr->aux_w4next = 1;
/* See if this was already scheduled from LLL. If so, store aux context
* in global scan struct so we can pick it when scanned node is received
* with a valid context.
@ -575,6 +632,7 @@ ull_scan_aux_rx_flush:
aux->rx_last->rx_ftr.extra = rx;
} else {
LL_ASSERT(sync_lll);
ll_rx_put(link, rx);
ll_rx_sched();
}
@ -698,8 +756,12 @@ void ull_scan_aux_release(memq_link_t *link, struct node_rx_hdr *rx)
lll_aux = rx->rx_ftr.param;
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
} else if (ull_sync_is_valid_get(param_ull)) {
struct ll_sync_set *sync;
struct lll_sync *lll;
sync = param_ull;
sync->data_len = 0U;
lll = rx->rx_ftr.param;
lll_aux = lll->lll_aux;
@ -707,7 +769,7 @@ void ull_scan_aux_release(memq_link_t *link, struct node_rx_hdr *rx)
* data properly.
*/
rx->type = NODE_RX_TYPE_SYNC_REPORT;
rx->handle = ull_sync_handle_get(param_ull);
rx->handle = ull_sync_handle_get(sync);
/* Dequeue will try releasing list of node rx, set the extra
* pointer to NULL.

View File

@ -44,4 +44,6 @@ struct ll_scan_aux_set {
struct node_rx_hdr *rx_head;
struct node_rx_hdr *rx_last;
uint16_t data_len;
};

View File

@ -477,6 +477,9 @@ void ull_sync_release(struct ll_sync_set *sync)
/* Mark sync context not sync established */
sync->timeout_reload = 0U;
/* reset accumulated data len */
sync->data_len = 0U;
mem_release(sync, &sync_free);
}

View File

@ -74,6 +74,8 @@ struct ll_sync_set {
struct ll_sync_iso_set *volatile sync_iso;
} iso;
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
uint16_t data_len;
};
struct node_rx_sync {