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 uint8_t hdr_data[ULL_ADV_HDR_DATA_LEN_SIZE +
144 ULL_ADV_HDR_DATA_AUX_PTR_PTR_SIZE];
145 struct pdu_adv *pdu_prev, *pdu, *pdu_new;
146 char pdu_buff[PDU_PAULOAD_BUFF_SIZE];
147 void *extra_data_prev, *extra_data;
148 struct lll_adv_sync *lll_sync;
149 bool adi_in_sync_ind;
150 uint8_t err, pdu_idx;
151
152 lll_sync = adv_set->lll.sync;
153 pdu = lll_adv_sync_data_peek(lll_sync, NULL);
154 ull_adv_sync_pdu_init(pdu, 0U, 0U, 0U, NULL);
155
156 err = ull_adv_sync_pdu_alloc(adv_set, ULL_ADV_PDU_EXTRA_DATA_ALLOC_IF_EXIST, &pdu_prev,
157 &pdu, &extra_data_prev, &extra_data, &pdu_idx);
158 zassert_equal(err, 0, "Unexpected error while PDU allocation, err: %d", err);
159
160 if (extra_data) {
161 ull_adv_sync_extra_data_set_clear(extra_data_prev, extra_data, 0, 0, NULL);
162 }
163
164 /* Create AUX_SYNC_IND PDU as a head of chain */
165 err = ull_adv_sync_pdu_set_clear(lll_sync, pdu_prev, pdu,
166 (pdu_count > 1 ? ULL_ADV_PDU_HDR_FIELD_AUX_PTR :
167 ULL_ADV_PDU_HDR_FIELD_NONE),
168 ULL_ADV_PDU_HDR_FIELD_NONE, hdr_data);
169 zassert_equal(err, 0, "Unexpected error during initialization of extended PDU, err: %d",
170 err);
171
172 if (IS_ENABLED(CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT)) {
173 adi_in_sync_ind = ull_adv_sync_pdu_had_adi(pdu);
174 }
175
176 /* Add some AD for testing */
177 snprintf(pdu_buff, ARRAY_SIZE(pdu_buff), "test%" PRIu8 " test%" PRIu8 " test%" PRIu8 "", 0,
178 0, 0);
179 common_pdu_adv_data_set(pdu, pdu_buff, strlen(pdu_buff));
180 /* Create AUX_CHAIN_IND PDUs. Start from 1, AUX_SYNC_IND is first PDU. */
181 for (uint8_t idx = 1; idx < pdu_count; ++idx) {
182 snprintf(pdu_buff, ARRAY_SIZE(pdu_buff),
183 "test%" PRIu8 " test%" PRIu8 " test%" PRIu8 "", idx, idx, idx);
184 /* Allocate new PDU */
185 pdu_new = lll_adv_pdu_alloc_pdu_adv();
186 zassert_not_null(pdu_new, "Cannot allocate new PDU.");
187 /* Initialize new empty PDU. Last AUX_CHAIN_IND may not include AuxPtr. */
188 if (idx < pdu_count - 1) {
189 if (IS_ENABLED(CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT) &&
190 adi_in_sync_ind) {
191 ull_adv_sync_pdu_init(pdu_new,
192 ULL_ADV_PDU_HDR_FIELD_AUX_PTR |
193 ULL_ADV_PDU_HDR_FIELD_ADI,
194 lll_sync->adv->phy_s,
195 lll_sync->adv->phy_flags, NULL);
196 } else {
197 ull_adv_sync_pdu_init(pdu_new,
198 ULL_ADV_PDU_HDR_FIELD_AUX_PTR,
199 lll_sync->adv->phy_s,
200 lll_sync->adv->phy_flags, NULL);
201 }
202 } else {
203 if (IS_ENABLED(CONFIG_BT_CTLR_ADV_PERIODIC_ADI_SUPPORT) &&
204 adi_in_sync_ind) {
205 ull_adv_sync_pdu_init(pdu_new,
206 ULL_ADV_PDU_HDR_FIELD_ADI,
207 0U, 0U, NULL);
208 } else {
209 ull_adv_sync_pdu_init(pdu_new,
210 ULL_ADV_PDU_HDR_FIELD_NONE,
211 0U, 0U, NULL);
212 }
213 }
214 /* Add some AD for testing */
215 common_pdu_adv_data_set(pdu_new, pdu_buff, strlen(pdu_buff));
216 /* Link to previous PDU */
217 lll_adv_pdu_linked_append(pdu_new, pdu);
218 pdu = pdu_new;
219 }
220
221 lll_adv_sync_data_enqueue(lll_sync, pdu_idx);
222 }
223
224 /*
225 * @brief Helper function to release periodic advertising chain that was enqueued for
226 * advertising set.
227 *
228 * @param adv_set Pointer to advertising set to release a PDUs chain.
229 */
common_release_per_adv_chain(struct ll_adv_set * adv_set)230 void common_release_per_adv_chain(struct ll_adv_set *adv_set)
231 {
232 struct lll_adv_sync *lll_sync;
233 struct pdu_adv *pdu;
234
235 lll_sync = adv_set->lll.sync;
236 pdu = lll_adv_sync_data_peek(lll_sync, NULL);
237 if (pdu != NULL) {
238 lll_adv_pdu_linked_release_all(pdu);
239 }
240
241 pdu = (void *)lll_sync->data.pdu[lll_sync->data.first];
242 if (pdu != NULL) {
243 lll_adv_pdu_linked_release_all(pdu);
244 }
245 }
246
247 /*
248 * @brief Helper function that validates content of periodic advertising PDU.
249 *
250 * The function verifies if content of periodic advertising PDU as as expected. The function
251 * verifies two types of PDUs: AUX_SYNC_IND and AUX_CHAIN_IND. AUX_CHAIN_IND is validated
252 * as if its superior PDU is AUX_SYNC_IND only.
253 *
254 * Expected fields in extended advertising header are provided by @p exp_ext_hdr_flags.
255 *
256 * Note: The function expects that there is no ACAD data in the PDU.
257 *
258 * @param pdu Pointer to PDU to be verified.
259 * @param type Type of the PDU.
260 * @param exp_ext_hdr_flags Bitfield with expected extended header flags.
261 */
common_validate_per_adv_pdu(struct pdu_adv * pdu,enum test_pdu_ext_adv_type type,uint16_t exp_ext_hdr_flags)262 void common_validate_per_adv_pdu(struct pdu_adv *pdu, enum test_pdu_ext_adv_type type,
263 uint16_t exp_ext_hdr_flags)
264 {
265 struct pdu_adv_com_ext_adv *com_hdr;
266 struct pdu_adv_ext_hdr *ext_hdr;
267 uint8_t ext_hdr_len;
268 uint8_t *dptr;
269
270 if (pdu->len > 1) {
271 com_hdr = &pdu->adv_ext_ind;
272 /* Non-connectable and Non-scannable adv mode */
273 zassert_equal(com_hdr->adv_mode, 0U,
274 "Unexpected mode of extended advertising PDU: %" PRIu8,
275 com_hdr->adv_mode);
276
277 ext_hdr = &com_hdr->ext_hdr;
278 dptr = ext_hdr->data;
279
280 if (com_hdr->ext_hdr_len > 0) {
281 zassert_false(ext_hdr->adv_addr,
282 "Unexpected AdvA field in extended advertising header");
283 zassert_false(ext_hdr->tgt_addr,
284 "Unexpected TargetA field in extended advertising header");
285 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_CTE_INFO) {
286 zassert_true(
287 ext_hdr->cte_info,
288 "Missing expected CteInfo field in extended advertising header");
289 dptr += sizeof(struct pdu_cte_info);
290 } else {
291 zassert_false(
292 ext_hdr->cte_info,
293 "Unexpected CteInfo field in extended advertising header");
294 }
295 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_ADI) {
296 zassert_true(
297 ext_hdr->adi,
298 "Missing expected ADI field in extended advertising header");
299 dptr += sizeof(struct pdu_adv_adi);
300 } else {
301 zassert_false(
302 ext_hdr->adi,
303 "Unexpected ADI field in extended advertising header");
304 }
305 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_AUX_PTR) {
306 zassert_true(
307 ext_hdr->aux_ptr,
308 "Missing expected AuxPtr field in extended advertising header");
309 dptr += sizeof(struct pdu_adv_aux_ptr);
310 } else {
311 zassert_false(
312 ext_hdr->aux_ptr,
313 "Unexpected AuxPtr field in extended advertising header");
314 }
315 zassert_false(ext_hdr->sync_info,
316 "Unexpected SyncInfo field in extended advertising header");
317 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_TX_POWER) {
318 zassert_true(
319 ext_hdr->tx_pwr,
320 "Missing expected CteInfo field in extended advertising header");
321 dptr += sizeof(uint8_t);
322 } else {
323 zassert_false(
324 ext_hdr->tx_pwr,
325 "Unexpected CteInfo field in extended advertising header");
326 }
327
328 /* Calculate extended header len, ACAD should not be available.
329 * ull_adv_aux_hdr_len_calc returns ext hdr length without it.
330 */
331 ext_hdr_len = ull_adv_aux_hdr_len_calc(com_hdr, &dptr);
332 zassert_equal(com_hdr->ext_hdr_len,
333 ext_hdr_len - PDU_AC_EXT_HEADER_SIZE_MIN,
334 "Extended header length: %" PRIu8
335 " different than expected %" PRIu8,
336 ext_hdr_len, com_hdr->ext_hdr_len);
337
338 if (exp_ext_hdr_flags & ULL_ADV_PDU_HDR_FIELD_AD_DATA) {
339 zassert_true((pdu->len - ext_hdr_len) > 0,
340 "Missing expected advertising data in PDU");
341 } else {
342 zassert_equal(pdu->len - ext_hdr_len, 0,
343 "Unexpected advertising data in PDU");
344 }
345 } else {
346 zassert_equal(exp_ext_hdr_flags, ULL_ADV_PDU_HDR_FIELD_AD_DATA,
347 "Unexpected extended header flags: %" PRIu16,
348 exp_ext_hdr_flags);
349 }
350 }
351 }
352
353 /*
354 * @brief Helper function to prepare CTE configuration for a given advertising set.
355 *
356 * Note: There is a single instance of CTE configuration. In case there is a need
357 * to use multiple advertising sets at once, all will use the same CTE configuration.
358 *
359 * @param adv Pointer to advertising set
360 * @param cte_count Requested number of CTEs to be send
361 */
common_prepare_df_cfg(struct ll_adv_set * adv,uint8_t cte_count)362 void common_prepare_df_cfg(struct ll_adv_set *adv, uint8_t cte_count)
363 {
364 /* Prepare CTE configuration */
365 g_df_cfg.cte_count = cte_count;
366 g_df_cfg.cte_length = TEST_CTE_LENGTH;
367 g_df_cfg.cte_type = BT_HCI_LE_AOD_CTE_2US;
368
369 adv->df_cfg = &g_df_cfg;
370 }
371
372 /*
373 * @brief Helper function that validates correctness of periodic advertising chain.
374 *
375 * The function expects that all periodic advertising chain PDUs will have advertising data.
376 *
377 * @param adv Pointer to advertising set
378 * @param pdu_count Number of PDUs in a chain
379 */
common_validate_per_adv_chain(struct ll_adv_set * adv,uint8_t pdu_count)380 void common_validate_per_adv_chain(struct ll_adv_set *adv, uint8_t pdu_count)
381 {
382 uint16_t ext_hdr_flags;
383 struct pdu_adv *pdu;
384
385 pdu = lll_adv_sync_data_peek(adv->lll.sync, NULL);
386
387 /* Validate AUX_SYNC_IND */
388 if (pdu_count > 1) {
389 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AUX_PTR | ULL_ADV_PDU_HDR_FIELD_AD_DATA;
390 } else {
391 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AD_DATA;
392 }
393
394 common_validate_per_adv_pdu(pdu, TEST_PDU_EXT_ADV_SYNC_IND, ext_hdr_flags);
395 pdu = lll_adv_pdu_linked_next_get(pdu);
396 if (pdu_count > 1) {
397 zassert_not_null(pdu, "Expected PDU in periodic advertising chain is NULL");
398 } else {
399 zassert_is_null(pdu, "Unexpected PDU in a single PDU periodic advertising chain");
400 }
401 /* Validate AUX_CHAIN_IND PDUs in a periodic advertising chain. Start from 1, because
402 * first PDU is AUX_SYNC_IND.
403 */
404 for (uint8_t idx = 1; idx < pdu_count; ++idx) {
405 if (idx != (pdu_count - 1)) {
406 ext_hdr_flags =
407 ULL_ADV_PDU_HDR_FIELD_AUX_PTR | ULL_ADV_PDU_HDR_FIELD_AD_DATA;
408 } else {
409 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AD_DATA;
410 }
411
412 common_validate_per_adv_pdu(pdu, TEST_PDU_EXT_ADV_CHAIN_IND, ext_hdr_flags);
413 pdu = lll_adv_pdu_linked_next_get(pdu);
414 if (idx != (pdu_count - 1)) {
415 zassert_not_null(pdu, "Expected PDU in periodic advertising chain is NULL");
416 } else {
417 zassert_is_null(pdu, "Unexpected PDU at end of periodic advertising chain");
418 }
419 }
420 }
421
422 /*
423 * @brief Helper function that validates correctness of periodic advertising chain including CTE
424 *
425 * The number of PDUs including advertising data or CTE is provided by appropriate function
426 * arguments. PUDs including CTE are always first #N PDUs. The same rule applies to PDUs including
427 * advertising data. So maximum number of PDUs in a chain is maximum value from pair @p cte_count
428 * and @p ad_data_count.
429 *
430 * @param adv Pointer to advertising set
431 * @param cte_count Number of PDUs including CTE
432 * @param ad_data_pdu_count Number of PDUs including advertising data
433 */
common_validate_chain_with_cte(struct ll_adv_set * adv,uint8_t cte_count,uint8_t ad_data_pdu_count)434 void common_validate_chain_with_cte(struct ll_adv_set *adv, uint8_t cte_count,
435 uint8_t ad_data_pdu_count)
436 {
437 uint16_t ext_hdr_flags;
438 struct pdu_adv *pdu;
439 uint8_t pdu_count;
440
441 pdu = lll_adv_sync_data_peek(adv->lll.sync, NULL);
442 if (cte_count > 1) {
443 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AUX_PTR | ULL_ADV_PDU_HDR_FIELD_CTE_INFO;
444
445 } else {
446 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_CTE_INFO;
447 }
448 if (ad_data_pdu_count > 0) {
449 ext_hdr_flags |= ULL_ADV_PDU_HDR_FIELD_AD_DATA;
450 }
451
452 common_validate_per_adv_pdu(pdu, TEST_PDU_EXT_ADV_SYNC_IND, ext_hdr_flags);
453
454 pdu_count = MAX(cte_count, ad_data_pdu_count);
455
456 pdu = lll_adv_pdu_linked_next_get(pdu);
457 if (pdu_count > 1) {
458 zassert_not_null(pdu, "Expected PDU in periodic advertising chain is NULL");
459 } else {
460 zassert_is_null(pdu, "Unexpected PDU in a single PDU periodic advertising chain");
461 }
462
463 for (uint8_t idx = 1; idx < pdu_count; ++idx) {
464 if (idx < pdu_count - 1) {
465 ext_hdr_flags = ULL_ADV_PDU_HDR_FIELD_AUX_PTR;
466 } else {
467 ext_hdr_flags = 0U;
468 }
469 if (idx < cte_count) {
470 ext_hdr_flags |= ULL_ADV_PDU_HDR_FIELD_CTE_INFO;
471 }
472 if (idx < ad_data_pdu_count) {
473 ext_hdr_flags |= ULL_ADV_PDU_HDR_FIELD_AD_DATA;
474 }
475
476 common_validate_per_adv_pdu(pdu, TEST_PDU_EXT_ADV_CHAIN_IND, ext_hdr_flags);
477
478 pdu = lll_adv_pdu_linked_next_get(pdu);
479 if (idx < (pdu_count - 1)) {
480 zassert_not_null(pdu, "Expected PDU in periodic advertising chain is NULL");
481 } else {
482 zassert_is_null(pdu, "Unexpected PDU at end of periodic advertising chain");
483 }
484 }
485 }
486
487 /*
488 * @brief Helper function to cleanup after test case end.
489 *
490 * @param adv Pointer to advertising set
491 */
common_teardown(struct ll_adv_set * adv)492 void common_teardown(struct ll_adv_set *adv)
493 {
494 common_release_per_adv_chain(adv);
495 common_release_adv_set(adv);
496 lll_adv_init();
497 }
498 /*
499 * @brief Helper function to add payload data to extended advertising PDU.
500 *
501 * @param pdu Pointer to extended advertising PDU.
502 * @param data Pointer to data to be added.
503 * @param len Length of the data.
504 */
common_pdu_adv_data_set(struct pdu_adv * pdu,const uint8_t * data,uint8_t len)505 static void common_pdu_adv_data_set(struct pdu_adv *pdu, const uint8_t *data, uint8_t len)
506 {
507 struct pdu_adv_com_ext_adv *com_hdr;
508 uint8_t len_max;
509 uint8_t *dptr;
510
511 com_hdr = &pdu->adv_ext_ind;
512
513 dptr = &com_hdr->ext_hdr_adv_data[com_hdr->ext_hdr_len];
514
515 len_max = PDU_AC_PAYLOAD_SIZE_MAX - (dptr - pdu->payload);
516 zassert_false(len > len_max,
517 "Provided data length exceeds maximum supported payload length: %" PRIu8,
518 len_max);
519
520 memcpy(dptr, data, len);
521 dptr += len;
522
523 pdu->len = dptr - pdu->payload;
524 }
525