1 /*
2 * Copyright (c) 2021 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <stddef.h>
9 #include <zephyr/ztest.h>
10
11 #include <zephyr/bluetooth/bluetooth.h>
12 #include <zephyr/bluetooth/hci.h>
13 #include <zephyr/sys/byteorder.h>
14 #include <host/hci_core.h>
15
16 #include "util/util.h"
17 #include "util/memq.h"
18 #include "util/mem.h"
19 #include "util/dbuf.h"
20
21 #include "pdu_df.h"
22 #include "lll/pdu_vendor.h"
23 #include "pdu.h"
24
25 #include "hal/ccm.h"
26
27 #include "lll.h"
28 #include "lll/lll_adv_types.h"
29 #include "lll_adv.h"
30 #include "lll/lll_adv_pdu.h"
31 #include "lll_adv_sync.h"
32 #include "lll/lll_df_types.h"
33
34 #include "ull_adv_types.h"
35 #include "ull_adv_internal.h"
36
37 #include "ll.h"
38 #include "common.h"
39
40 #define PDU_PAULOAD_BUFF_SIZE 100
41 #define TEST_CTE_LENGTH 20
42
43 /* Controller code uses static function sync_acquire to get adv. sync.
44 * For test purposes it is used as global variable to avoid complete
45 * creation of advertising set.
46 */
47 static struct ll_adv_sync_set g_sync_set;
48 static struct lll_df_adv_cfg g_df_cfg;
49
50 static void common_pdu_adv_data_set(struct pdu_adv *pdu, const uint8_t *data, uint8_t len);
51
52 /*
53 * @brief Helper function to create advertising set.
54 *
55 * The function creates advertising set to an extent required to test adding CTE to periodic
56 * advertising chains. The function returns handle to advertising set that may be used
57 * in calls to ULL functions related with advertising.
58 *
59 * @param hci_handle equivalent of a handle received from HCI command.
60 *
61 * @return Handle to created advertising set.
62 */
common_create_adv_set(uint8_t hci_handle)63 struct ll_adv_set *common_create_adv_set(uint8_t hci_handle)
64 {
65 struct lll_adv_sync *lll_sync;
66 struct ll_adv_set *adv_set;
67 uint8_t handle;
68 uint8_t err;
69
70 zassert_true(hci_handle < BT_HCI_LE_ADV_HANDLE_MAX,
71 "Advertising set handle: %" PRIu8 " exceeds maximum handles value %" PRIu8,
72 hci_handle, BT_HCI_LE_ADV_HANDLE_MAX);
73 err = ll_adv_set_by_hci_handle_get_or_new(hci_handle, &handle);
74 zassert_equal(err, 0, "Unexpected error while create new advertising set, err: %" PRIu8,
75 err);
76
77 adv_set = ull_adv_set_get(handle);
78 zassert_not_null(adv_set, 0, "Unexpectedly advertising set is NULL");
79 /* Note: there is single lll_adv_sync instance. If created more than one advertising set
80 * all will have reference to the same lll_adv_sync instance.
81 */
82 lll_sync = &g_sync_set.lll;
83 adv_set->lll.sync = &g_sync_set.lll;
84 lll_hdr_init(&adv_set->lll, adv_set);
85 g_sync_set.lll.adv = &adv_set->lll;
86 lll_hdr_init(lll_sync, &g_sync_set);
87
88 err = lll_adv_init();
89 zassert_equal(err, 0, "Unexpected error while initialization advertising set, err: %d",
90 err);
91
92 lll_hdr_init(lll_sync, &g_sync_set);
93
94 lll_adv_data_reset(&lll_sync->data);
95 err = lll_adv_data_init(&lll_sync->data);
96 zassert_equal(err, 0,
97 "Unexpected error while initialization advertising data init, err: %d", err);
98
99 adv_set->is_created = 1U;
100
101 return adv_set;
102 }
103
104 /*
105 * @brief Release advertising set.
106 *
107 * @param adv_set Pointer to advertising set to be released.
108 */
common_release_adv_set(struct ll_adv_set * adv_set)109 void common_release_adv_set(struct ll_adv_set *adv_set)
110 {
111 struct ll_adv_sync_set *sync;
112
113 if (adv_set->lll.sync) {
114 sync = HDR_LLL2ULL(adv_set->lll.sync);
115 if (sync) {
116 sync->is_started = 0U;
117 }
118
119 lll_adv_data_reset(&sync->lll.data);
120 }
121 adv_set->lll.sync = NULL;
122 if (adv_set->df_cfg->is_enabled) {
123 adv_set->df_cfg->is_enabled = 0U;
124 }
125 adv_set->df_cfg = NULL;
126 adv_set->is_created = 0U;
127 }
128
129 /*
130 * @brief Helper function that creates periodic advertising chain.
131 *
132 * The function creates periodic advertising chain with provided number of PDUs @p pdu_count.
133 * The created chain is enqueued in provided advertising set. Number of requested PDUs includes
134 * head of a chain AUX_SYNC_IND.
135 * Each created PDU will hold payload data in following pattern:
136 * "test%d test%d test%d", where '%d' is substituted by PDU index.
137 *
138 * @param adv_set Pointer to advertising set to enqueue created chain.
139 * @param pdu_count Requested number of PDUs in a chain.
140 */
common_create_per_adv_chain(struct ll_adv_set * adv_set,uint8_t pdu_count)141 void common_create_per_adv_chain(struct ll_adv_set *adv_set, uint8_t pdu_count)
142 {
143 struct pdu_adv *pdu_prev, *pdu, *pdu_new;
144 char pdu_buff[PDU_PAULOAD_BUFF_SIZE];
145 void *extra_data_prev, *extra_data;
146 struct lll_adv_sync *lll_sync;
147 bool adi_in_sync_ind;
148 uint8_t err, pdu_idx;
149
150 lll_sync = adv_set->lll.sync;
151 pdu = lll_adv_sync_data_peek(lll_sync, NULL);
152 ull_adv_sync_pdu_init(pdu, 0U, 0U, 0U, NULL);
153
154 err = ull_adv_sync_pdu_alloc(adv_set, ULL_ADV_PDU_EXTRA_DATA_ALLOC_IF_EXIST, &pdu_prev,
155 &pdu, &extra_data_prev, &extra_data, &pdu_idx);
156 zassert_equal(err, 0, "Unexpected error while PDU allocation, err: %d", err);
157
158 if (extra_data) {
159 ull_adv_sync_extra_data_set_clear(extra_data_prev, extra_data, 0, 0, NULL);
160 }
161
162 if (IS_ENABLED(CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT)) {
163 adi_in_sync_ind = ull_adv_sync_pdu_had_adi(pdu_prev);
164 }
165
166 /* Create AUX_SYNC_IND PDU as a head of chain */
167 if (IS_ENABLED(CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT) && adi_in_sync_ind) {
168 ull_adv_sync_pdu_init(pdu, (pdu_count > 1 ? ULL_ADV_PDU_HDR_FIELD_AUX_PTR |
169 ULL_ADV_PDU_HDR_FIELD_ADI :
170 ULL_ADV_PDU_HDR_FIELD_ADI),
171 lll_sync->adv->phy_s,
172 lll_sync->adv->phy_flags, NULL);
173
174 } else {
175 ull_adv_sync_pdu_init(pdu, (pdu_count > 1 ? ULL_ADV_PDU_HDR_FIELD_AUX_PTR :
176 ULL_ADV_PDU_HDR_FIELD_NONE),
177 lll_sync->adv->phy_s,
178 lll_sync->adv->phy_flags, NULL);
179
180 }
181
182 /* Add some AD for testing */
183 snprintf(pdu_buff, ARRAY_SIZE(pdu_buff), "test%" PRIu8 " test%" PRIu8 " test%" PRIu8 "", 0,
184 0, 0);
185 common_pdu_adv_data_set(pdu, pdu_buff, strlen(pdu_buff));
186 /* Create AUX_CHAIN_IND PDUs. Start from 1, AUX_SYNC_IND is first PDU. */
187 for (uint8_t idx = 1; idx < pdu_count; ++idx) {
188 snprintf(pdu_buff, ARRAY_SIZE(pdu_buff),
189 "test%" PRIu8 " test%" PRIu8 " test%" PRIu8 "", idx, idx, idx);
190 /* Allocate new PDU */
191 pdu_new = lll_adv_pdu_alloc_pdu_adv();
192 zassert_not_null(pdu_new, "Cannot allocate new PDU.");
193 /* Initialize new empty PDU. Last AUX_CHAIN_IND may not include AuxPtr. */
194 if (idx < pdu_count - 1) {
195 if (IS_ENABLED(CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT) &&
196 adi_in_sync_ind) {
197 ull_adv_sync_pdu_init(pdu_new,
198 ULL_ADV_PDU_HDR_FIELD_AUX_PTR |
199 ULL_ADV_PDU_HDR_FIELD_ADI,
200 lll_sync->adv->phy_s,
201 lll_sync->adv->phy_flags, NULL);
202 } else {
203 ull_adv_sync_pdu_init(pdu_new,
204 ULL_ADV_PDU_HDR_FIELD_AUX_PTR,
205 lll_sync->adv->phy_s,
206 lll_sync->adv->phy_flags, NULL);
207 }
208 } else {
209 if (IS_ENABLED(CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT) &&
210 adi_in_sync_ind) {
211 ull_adv_sync_pdu_init(pdu_new,
212 ULL_ADV_PDU_HDR_FIELD_ADI,
213 0U, 0U, NULL);
214 } else {
215 ull_adv_sync_pdu_init(pdu_new,
216 ULL_ADV_PDU_HDR_FIELD_NONE,
217 0U, 0U, NULL);
218 }
219 }
220 /* Add some AD for testing */
221 common_pdu_adv_data_set(pdu_new, pdu_buff, strlen(pdu_buff));
222 /* Link to previous PDU */
223 lll_adv_pdu_linked_append(pdu_new, pdu);
224 pdu = pdu_new;
225 }
226
227 lll_adv_sync_data_enqueue(lll_sync, pdu_idx);
228 }
229
230 /*
231 * @brief Helper function to release periodic advertising chain that was enqueued for
232 * advertising set.
233 *
234 * @param adv_set Pointer to advertising set to release a PDUs chain.
235 */
common_release_per_adv_chain(struct ll_adv_set * adv_set)236 void common_release_per_adv_chain(struct ll_adv_set *adv_set)
237 {
238 struct lll_adv_sync *lll_sync;
239 struct pdu_adv *pdu;
240
241 lll_sync = adv_set->lll.sync;
242 pdu = lll_adv_sync_data_peek(lll_sync, NULL);
243 if (pdu != NULL) {
244 lll_adv_pdu_linked_release_all(pdu);
245 }
246
247 pdu = (void *)lll_sync->data.pdu[lll_sync->data.first];
248 if (pdu != NULL) {
249 lll_adv_pdu_linked_release_all(pdu);
250 }
251 }
252
253 /*
254 * @brief Helper function that validates content of periodic advertising PDU.
255 *
256 * The function verifies if content of periodic advertising PDU as expected. The function
257 * verifies two types of PDUs: AUX_SYNC_IND and AUX_CHAIN_IND. AUX_CHAIN_IND is validated
258 * as if its superior PDU is AUX_SYNC_IND only.
259 *
260 * Expected fields in extended advertising header are provided by @p exp_ext_hdr_flags.
261 *
262 * Note: The function expects that there is no ACAD data in the PDU.
263 *
264 * @param pdu Pointer to PDU to be verified.
265 * @param type Type of the PDU.
266 * @param exp_ext_hdr_flags Bitfield with expected extended header flags.
267 */
common_validate_per_adv_pdu(struct pdu_adv * pdu,enum test_pdu_ext_adv_type type,uint16_t exp_ext_hdr_flags)268 void common_validate_per_adv_pdu(struct pdu_adv *pdu, enum test_pdu_ext_adv_type type,
269 uint16_t exp_ext_hdr_flags)
270 {
271 struct pdu_adv_com_ext_adv *com_hdr;
272 struct pdu_adv_ext_hdr *ext_hdr;
273 uint8_t ext_hdr_len;
274 uint8_t *dptr;
275
276 if (pdu->len > 1) {
277 com_hdr = &pdu->adv_ext_ind;
278 /* Non-connectable and Non-scannable adv mode */
279 zassert_equal(com_hdr->adv_mode, 0U,
280 "Unexpected mode of extended advertising PDU: %" PRIu8,
281 com_hdr->adv_mode);
282
283 ext_hdr = &com_hdr->ext_hdr;
284 dptr = ext_hdr->data;
285
286 if (com_hdr->ext_hdr_len > 0) {
287 zassert_false(ext_hdr->adv_addr,
288 "Unexpected AdvA field in extended advertising header");
289 zassert_false(ext_hdr->tgt_addr,
290 "Unexpected TargetA field in extended advertising header");
291 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_CTE_INFO) {
292 zassert_true(
293 ext_hdr->cte_info,
294 "Missing expected CteInfo field in extended advertising header");
295 dptr += sizeof(struct pdu_cte_info);
296 } else {
297 zassert_false(
298 ext_hdr->cte_info,
299 "Unexpected CteInfo field in extended advertising header");
300 }
301 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_ADI) {
302 zassert_true(
303 ext_hdr->adi,
304 "Missing expected ADI field in extended advertising header");
305 dptr += sizeof(struct pdu_adv_adi);
306 } else {
307 zassert_false(
308 ext_hdr->adi,
309 "Unexpected ADI field in extended advertising header");
310 }
311 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_AUX_PTR) {
312 zassert_true(
313 ext_hdr->aux_ptr,
314 "Missing expected AuxPtr field in extended advertising header");
315 dptr += sizeof(struct pdu_adv_aux_ptr);
316 } else {
317 zassert_false(
318 ext_hdr->aux_ptr,
319 "Unexpected AuxPtr field in extended advertising header");
320 }
321 zassert_false(ext_hdr->sync_info,
322 "Unexpected SyncInfo field in extended advertising header");
323 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_TX_POWER) {
324 zassert_true(
325 ext_hdr->tx_pwr,
326 "Missing expected CteInfo field in extended advertising header");
327 dptr += sizeof(uint8_t);
328 } else {
329 zassert_false(
330 ext_hdr->tx_pwr,
331 "Unexpected CteInfo field in extended advertising header");
332 }
333
334 /* Calculate extended header len, ACAD should not be available.
335 * ull_adv_aux_hdr_len_calc returns ext hdr length without it.
336 */
337 ext_hdr_len = ull_adv_aux_hdr_len_calc(com_hdr, &dptr);
338 zassert_equal(com_hdr->ext_hdr_len,
339 ext_hdr_len - PDU_AC_EXT_HEADER_SIZE_MIN,
340 "Extended header length: %" PRIu8
341 " different than expected %" PRIu8,
342 ext_hdr_len, com_hdr->ext_hdr_len);
343
344 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_AD_DATA) {
345 zassert_true((pdu->len - ext_hdr_len) > 0,
346 "Missing expected advertising data in PDU");
347 } else {
348 zassert_equal(pdu->len - ext_hdr_len, 0,
349 "Unexpected advertising data in PDU");
350 }
351 } else {
352 zassert_equal(exp_ext_hdr_flags, ULL_ADV_PDU_HDR_FIELD_AD_DATA,
353 "Unexpected extended header flags: %" PRIu16,
354 exp_ext_hdr_flags);
355 }
356 }
357 }
358
359 /*
360 * @brief Helper function to prepare CTE configuration for a given advertising set.
361 *
362 * Note: There is a single instance of CTE configuration. In case there is a need
363 * to use multiple advertising sets at once, all will use the same CTE configuration.
364 *
365 * @param adv Pointer to advertising set
366 * @param cte_count Requested number of CTEs to be send
367 */
common_prepare_df_cfg(struct ll_adv_set * adv,uint8_t cte_count)368 void common_prepare_df_cfg(struct ll_adv_set *adv, uint8_t cte_count)
369 {
370 /* Prepare CTE configuration */
371 g_df_cfg.cte_count = cte_count;
372 g_df_cfg.cte_length = TEST_CTE_LENGTH;
373 g_df_cfg.cte_type = BT_HCI_LE_AOD_CTE_2US;
374
375 adv->df_cfg = &g_df_cfg;
376 }
377
378 /*
379 * @brief Helper function that validates correctness of periodic advertising chain.
380 *
381 * The function expects that all periodic advertising chain PDUs will have advertising data.
382 *
383 * @param adv Pointer to advertising set
384 * @param pdu_count Number of PDUs in a chain
385 */
common_validate_per_adv_chain(struct ll_adv_set * adv,uint8_t pdu_count)386 void common_validate_per_adv_chain(struct ll_adv_set *adv, uint8_t pdu_count)
387 {
388 uint16_t ext_hdr_flags;
389 struct pdu_adv *pdu;
390
391 pdu = lll_adv_sync_data_peek(adv->lll.sync, NULL);
392
393 /* Validate AUX_SYNC_IND */
394 if (pdu_count > 1) {
395 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AUX_PTR | ULL_ADV_PDU_HDR_FIELD_AD_DATA;
396 } else {
397 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AD_DATA;
398 }
399
400 common_validate_per_adv_pdu(pdu, TEST_PDU_EXT_ADV_SYNC_IND, ext_hdr_flags);
401 pdu = lll_adv_pdu_linked_next_get(pdu);
402 if (pdu_count > 1) {
403 zassert_not_null(pdu, "Expected PDU in periodic advertising chain is NULL");
404 } else {
405 zassert_is_null(pdu, "Unexpected PDU in a single PDU periodic advertising chain");
406 }
407 /* Validate AUX_CHAIN_IND PDUs in a periodic advertising chain. Start from 1, because
408 * first PDU is AUX_SYNC_IND.
409 */
410 for (uint8_t idx = 1; idx < pdu_count; ++idx) {
411 if (idx != (pdu_count - 1)) {
412 ext_hdr_flags =
413 ULL_ADV_PDU_HDR_FIELD_AUX_PTR | ULL_ADV_PDU_HDR_FIELD_AD_DATA;
414 } else {
415 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AD_DATA;
416 }
417
418 common_validate_per_adv_pdu(pdu, TEST_PDU_EXT_ADV_CHAIN_IND, ext_hdr_flags);
419 pdu = lll_adv_pdu_linked_next_get(pdu);
420 if (idx != (pdu_count - 1)) {
421 zassert_not_null(pdu, "Expected PDU in periodic advertising chain is NULL");
422 } else {
423 zassert_is_null(pdu, "Unexpected PDU at end of periodic advertising chain");
424 }
425 }
426 }
427
428 /*
429 * @brief Helper function that validates correctness of periodic advertising chain including CTE
430 *
431 * The number of PDUs including advertising data or CTE is provided by appropriate function
432 * arguments. PUDs including CTE are always first #N PDUs. The same rule applies to PDUs including
433 * advertising data. So maximum number of PDUs in a chain is maximum value from pair @p cte_count
434 * and @p ad_data_count.
435 *
436 * @param adv Pointer to advertising set
437 * @param cte_count Number of PDUs including CTE
438 * @param ad_data_pdu_count Number of PDUs including advertising data
439 */
common_validate_chain_with_cte(struct ll_adv_set * adv,uint8_t cte_count,uint8_t ad_data_pdu_count)440 void common_validate_chain_with_cte(struct ll_adv_set *adv, uint8_t cte_count,
441 uint8_t ad_data_pdu_count)
442 {
443 uint16_t ext_hdr_flags;
444 struct pdu_adv *pdu;
445 uint8_t pdu_count;
446
447 pdu = lll_adv_sync_data_peek(adv->lll.sync, NULL);
448 if (cte_count > 1) {
449 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AUX_PTR | ULL_ADV_PDU_HDR_FIELD_CTE_INFO;
450
451 } else {
452 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_CTE_INFO;
453 }
454 if (ad_data_pdu_count > 0) {
455 ext_hdr_flags |= ULL_ADV_PDU_HDR_FIELD_AD_DATA;
456 }
457
458 common_validate_per_adv_pdu(pdu, TEST_PDU_EXT_ADV_SYNC_IND, ext_hdr_flags);
459
460 pdu_count = MAX(cte_count, ad_data_pdu_count);
461
462 pdu = lll_adv_pdu_linked_next_get(pdu);
463 if (pdu_count > 1) {
464 zassert_not_null(pdu, "Expected PDU in periodic advertising chain is NULL");
465 } else {
466 zassert_is_null(pdu, "Unexpected PDU in a single PDU periodic advertising chain");
467 }
468
469 for (uint8_t idx = 1; idx < pdu_count; ++idx) {
470 if (idx < pdu_count - 1) {
471 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AUX_PTR;
472 } else {
473 ext_hdr_flags = 0U;
474 }
475 if (idx < cte_count) {
476 ext_hdr_flags |= ULL_ADV_PDU_HDR_FIELD_CTE_INFO;
477 }
478 if (idx < ad_data_pdu_count) {
479 ext_hdr_flags |= ULL_ADV_PDU_HDR_FIELD_AD_DATA;
480 }
481
482 common_validate_per_adv_pdu(pdu, TEST_PDU_EXT_ADV_CHAIN_IND, ext_hdr_flags);
483
484 pdu = lll_adv_pdu_linked_next_get(pdu);
485 if (idx < (pdu_count - 1)) {
486 zassert_not_null(pdu, "Expected PDU in periodic advertising chain is NULL");
487 } else {
488 zassert_is_null(pdu, "Unexpected PDU at end of periodic advertising chain");
489 }
490 }
491 }
492
493 /*
494 * @brief Helper function to cleanup after test case end.
495 *
496 * @param adv Pointer to advertising set
497 */
common_teardown(struct ll_adv_set * adv)498 void common_teardown(struct ll_adv_set *adv)
499 {
500 common_release_per_adv_chain(adv);
501 common_release_adv_set(adv);
502 lll_adv_init();
503 }
504 /*
505 * @brief Helper function to add payload data to extended advertising PDU.
506 *
507 * @param pdu Pointer to extended advertising PDU.
508 * @param data Pointer to data to be added.
509 * @param len Length of the data.
510 */
common_pdu_adv_data_set(struct pdu_adv * pdu,const uint8_t * data,uint8_t len)511 static void common_pdu_adv_data_set(struct pdu_adv *pdu, const uint8_t *data, uint8_t len)
512 {
513 struct pdu_adv_com_ext_adv *com_hdr;
514 uint8_t len_max;
515 uint8_t *dptr;
516
517 com_hdr = &pdu->adv_ext_ind;
518
519 dptr = &com_hdr->ext_hdr_adv_data[com_hdr->ext_hdr_len];
520
521 len_max = PDU_AC_PAYLOAD_SIZE_MAX - (dptr - pdu->payload);
522 zassert_false(len > len_max,
523 "Provided data length exceeds maximum supported payload length: %" PRIu8,
524 len_max);
525
526 memcpy(dptr, data, len);
527 dptr += len;
528
529 pdu->len = dptr - pdu->payload;
530 }
531