Skip to content

Commit 6976b03

Browse files
cvinayakcarlescufi
authored andcommitted
Bluetooth: Controller: Fix handling of AD Data unchanged operation
Fix handling of AD Data set with operation type of unchanged data with respect to invalid parameter and state of Periodic Advertising. Signed-off-by: Vinayak Kariappa Chettimada <[email protected]>
1 parent a57e284 commit 6976b03

File tree

1 file changed

+62
-8
lines changed

1 file changed

+62
-8
lines changed

subsys/bluetooth/controller/ll_sw/ull_adv_sync.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -460,12 +460,9 @@ uint8_t ll_adv_sync_ad_data_set(uint8_t handle, uint8_t op, uint8_t len,
460460
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
461461
}
462462

463-
hdr_data[ULL_ADV_HDR_DATA_LEN_OFFSET] = len;
464-
(void)memcpy((void *)&hdr_data[ULL_ADV_HDR_DATA_DATA_PTR_OFFSET], &data,
465-
ULL_ADV_HDR_DATA_DATA_PTR_SIZE);
466-
467-
err = ull_adv_sync_pdu_alloc(adv, ULL_ADV_PDU_EXTRA_DATA_ALLOC_IF_EXIST, &pdu_prev, &pdu,
468-
&extra_data_prev, &extra_data, &ter_idx);
463+
err = ull_adv_sync_pdu_alloc(adv, ULL_ADV_PDU_EXTRA_DATA_ALLOC_IF_EXIST,
464+
&pdu_prev, &pdu, &extra_data_prev,
465+
&extra_data, &ter_idx);
469466
if (err) {
470467
return err;
471468
}
@@ -477,20 +474,61 @@ uint8_t ll_adv_sync_ad_data_set(uint8_t handle, uint8_t op, uint8_t len,
477474
}
478475
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
479476

480-
err = ull_adv_sync_pdu_set_clear(lll_sync, pdu_prev, pdu, ULL_ADV_PDU_HDR_FIELD_AD_DATA, 0,
477+
/* Use length = 0 and NULL pointer to retain old data in the PDU.
478+
* Use length = 0 and valid pointer of `data` (auto/local variable) to
479+
* remove old data.
480+
* User length > 0 and valid pointer of `data` (auto/local variable) to
481+
* set new data.
482+
*/
483+
if (op == BT_HCI_LE_EXT_ADV_OP_UNCHANGED_DATA) {
484+
hdr_data[ULL_ADV_HDR_DATA_LEN_OFFSET] = 0U;
485+
(void)memset((void *)&hdr_data[ULL_ADV_HDR_DATA_DATA_PTR_OFFSET],
486+
0U, ULL_ADV_HDR_DATA_DATA_PTR_SIZE);
487+
} else {
488+
hdr_data[ULL_ADV_HDR_DATA_LEN_OFFSET] = len;
489+
(void)memcpy((void *)&hdr_data[ULL_ADV_HDR_DATA_DATA_PTR_OFFSET],
490+
&data, ULL_ADV_HDR_DATA_DATA_PTR_SIZE);
491+
}
492+
493+
err = ull_adv_sync_pdu_set_clear(lll_sync, pdu_prev, pdu,
494+
ULL_ADV_PDU_HDR_FIELD_AD_DATA, 0,
481495
hdr_data);
482496
if (err) {
483497
return err;
484498
}
485499

500+
sync = HDR_LLL2ULL(lll_sync);
501+
502+
/* Parameter validation, if operation is 0x04 (unchanged data)
503+
* - periodic advertising is disabled, or
504+
* - periodic advertising contains no data, or
505+
* - Advertising Data Length is zero
506+
*/
507+
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK) &&
508+
(op == BT_HCI_LE_EXT_ADV_OP_UNCHANGED_DATA) &&
509+
((!sync->is_enabled) ||
510+
(hdr_data[ULL_ADV_HDR_DATA_LEN_OFFSET] == 0U) ||
511+
(len != 0U))) {
512+
/* NOTE: latest PDU was not consumed by LLL and as
513+
* ull_adv_sync_pdu_alloc() has reverted back the double buffer
514+
* with the first PDU, and returned the latest PDU as the new
515+
* PDU, we need to enqueue back the new PDU which is infact
516+
* the latest PDU.
517+
*/
518+
if (pdu_prev == pdu) {
519+
lll_adv_sync_data_enqueue(lll_sync, ter_idx);
520+
}
521+
522+
return BT_HCI_ERR_INVALID_PARAM;
523+
}
524+
486525
/* alloc() will return the same PDU as peek() in case there was PDU
487526
* queued but not switched to current before alloc() - no need to deal
488527
* with chain as it's already there. In other case we need to duplicate
489528
* chain from current PDU and append it to new PDU.
490529
*/
491530
adv_sync_pdu_chain_check_and_duplicate(pdu, pdu_prev);
492531

493-
sync = HDR_LLL2ULL(lll_sync);
494532
if (sync->is_started) {
495533
err = ull_adv_sync_time_update(sync, pdu);
496534
if (err) {
@@ -1302,10 +1340,26 @@ uint8_t ull_adv_sync_pdu_set_clear(struct lll_adv_sync *lll_sync,
13021340

13031341
/* Get Adv data from function parameters */
13041342
if (hdr_add_fields & ULL_ADV_PDU_HDR_FIELD_AD_DATA) {
1343+
uint8_t ad_len_prev;
1344+
13051345
ad_len = *(uint8_t *)hdr_data;
1346+
1347+
/* return prev ad data length */
1348+
ad_len_prev = ter_pdu_prev->len - ter_len_prev;
1349+
*(uint8_t *)hdr_data = ad_len_prev;
13061350
hdr_data = (uint8_t *)hdr_data + sizeof(ad_len);
13071351

1352+
/* remember the reference to new ad data */
13081353
(void)memcpy(&ad_data, hdr_data, sizeof(ad_data));
1354+
1355+
/* return the reference to prev ad data */
1356+
(void)memcpy(hdr_data, &ter_dptr, sizeof(ter_dptr));
1357+
1358+
/* unchanged data */
1359+
if (!ad_len && !ad_data) {
1360+
ad_len = ad_len_prev;
1361+
ad_data = ter_dptr_prev;
1362+
}
13091363
} else if (!(hdr_rem_fields & ULL_ADV_PDU_HDR_FIELD_AD_DATA)) {
13101364
ad_len = ter_pdu_prev->len - ter_len_prev;
13111365
ad_data = ter_dptr_prev;

0 commit comments

Comments
 (0)