1 /*
2 * Copyright (c) 2020 Demant
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <string.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include <zephyr/types.h>
12 #include <zephyr/ztest.h>
13 #include <zephyr/ztest_error_hook.h>
14
15 #include <zephyr/toolchain.h>
16 #include <zephyr/sys/util.h>
17
18 #include <zephyr/kernel.h>
19
20 #include <zephyr/bluetooth/bluetooth.h>
21 #include <zephyr/sys/byteorder.h>
22
23 #include "hal/cpu.h"
24 #include "hal/ccm.h"
25 #include "hal/cntr.h"
26 #include "hal/ticker.h"
27
28 #include "util/memq.h"
29
30 #include "pdu_df.h"
31 #include "lll/pdu_vendor.h"
32 #include "pdu.h"
33
34 #include "ll.h"
35 #include "lll.h"
36 #include "lll_conn_iso.h"
37 #include "lll_iso_tx.h"
38 #include "isoal.h"
39
40 #include "isoal_test_common.h"
41 #include "isoal_test_debug.h"
42
43 #define ULL_TIME_WRAPPING_POINT_US (HAL_TICKER_TICKS_TO_US_64BIT(HAL_TICKER_CNTR_MASK))
44 #define ULL_TIME_SPAN_FULL_US (ULL_TIME_WRAPPING_POINT_US + 1)
45
46 /**
47 * Intializes a RX PDU buffer
48 * @param[in] buf Pointer to buffer structure
49 */
isoal_test_init_rx_pdu_buffer(struct rx_pdu_meta_buffer * buf)50 void isoal_test_init_rx_pdu_buffer(struct rx_pdu_meta_buffer *buf)
51 {
52 memset(buf, 0, sizeof(struct rx_pdu_meta_buffer));
53 buf->pdu_meta.meta = &buf->meta;
54 buf->pdu_meta.pdu = (struct pdu_iso *) &buf->pdu[0];
55 }
56
57 /**
58 * Initializes a RX SDU buffer
59 * @param[in] buf Pointer to buffer structure
60 */
isoal_test_init_rx_sdu_buffer(struct rx_sdu_frag_buffer * buf)61 void isoal_test_init_rx_sdu_buffer(struct rx_sdu_frag_buffer *buf)
62 {
63 memset(buf, 0, sizeof(struct rx_sdu_frag_buffer));
64 }
65
66 /**
67 * Creates an unframed PDU fragment according to provided parameters
68 * @param[in] llid LLID as Start / Continue or Complete / End
69 * @param[in] dataptr Test data to fill PDU payload
70 * @param[in] length Length of PDU payload
71 * @param[in] payload_number Payload number (Meta Information)
72 * @param[in] timestamp PDU reception Time (Meta Information)
73 * @param[in] status PDU data validity
74 * @param[out] pdu_meta PDU buffer including meta structure
75 */
isoal_test_create_unframed_pdu(uint8_t llid,uint8_t * dataptr,uint8_t length,uint64_t payload_number,uint32_t timestamp,uint8_t status,struct isoal_pdu_rx * pdu_meta)76 void isoal_test_create_unframed_pdu(uint8_t llid,
77 uint8_t *dataptr,
78 uint8_t length,
79 uint64_t payload_number,
80 uint32_t timestamp,
81 uint8_t status,
82 struct isoal_pdu_rx *pdu_meta)
83 {
84 zassert_not_null(pdu_meta, "");
85 zassert_not_null(pdu_meta->meta, "");
86 zassert_not_null(pdu_meta->pdu, "");
87
88 memset(pdu_meta->meta, 0, sizeof(*pdu_meta->meta));
89 memset(pdu_meta->pdu, 0, sizeof(*pdu_meta->pdu));
90
91 pdu_meta->meta->payload_number = payload_number;
92 pdu_meta->meta->timestamp = timestamp;
93 pdu_meta->meta->status = status;
94
95 pdu_meta->pdu->ll_id = llid;
96 pdu_meta->pdu->len = length;
97 memcpy(pdu_meta->pdu->payload, dataptr, length);
98
99 isoal_test_debug_print_rx_pdu(pdu_meta);
100 }
101
102 /**
103 * Insert a new segment in the given PDU
104 * @param[In] sc !Start / Continuation bit
105 * @param[In] cmplt Complete bit
106 * @param[In] time_offset Time Offset (us)
107 * @param[In] dataptr Pointer to data to fill in segment
108 * @param[In] length Length of data
109 * @param[In/Out] pdu_meta PDU structure including meta information
110 * @return PDU data location index
111 */
isoal_test_insert_segment(bool sc,bool cmplt,uint32_t time_offset,uint8_t * dataptr,uint8_t length,struct isoal_pdu_rx * pdu_meta)112 uint16_t isoal_test_insert_segment(bool sc, bool cmplt, uint32_t time_offset, uint8_t *dataptr,
113 uint8_t length, struct isoal_pdu_rx *pdu_meta)
114 {
115 uint8_t seg_hdr[PDU_ISO_SEG_HDR_SIZE + PDU_ISO_SEG_TIMEOFFSET_SIZE];
116 uint16_t pdu_payload_size;
117 uint8_t hdr_write_size;
118 uint16_t pdu_data_loc;
119
120 pdu_payload_size = pdu_meta->pdu->len + length + PDU_ISO_SEG_HDR_SIZE +
121 (sc ? 0 : PDU_ISO_SEG_TIMEOFFSET_SIZE);
122 hdr_write_size = PDU_ISO_SEG_HDR_SIZE + (sc ? 0 : PDU_ISO_SEG_TIMEOFFSET_SIZE);
123 memset(&seg_hdr, 0, sizeof(seg_hdr));
124
125 zassert_true(pdu_payload_size <= TEST_RX_PDU_PAYLOAD_MAX,
126 "pdu_payload_size (%d)", pdu_payload_size);
127
128 /* Write header independent of endian dependent structures */
129 WRITE_BIT(seg_hdr[0], 0, sc); /* sc */
130 WRITE_BIT(seg_hdr[0], 1, cmplt); /* cmplt */
131 seg_hdr[1] = length + (sc ? 0 : PDU_ISO_SEG_TIMEOFFSET_SIZE);
132
133 if (!sc) {
134 sys_put_le24(time_offset, &seg_hdr[PDU_ISO_SEG_HDR_SIZE]);
135 }
136
137 memcpy(&pdu_meta->pdu->payload[pdu_meta->pdu->len], &seg_hdr, hdr_write_size);
138 pdu_meta->pdu->len += hdr_write_size;
139
140 memcpy(&pdu_meta->pdu->payload[pdu_meta->pdu->len], dataptr, length);
141 pdu_data_loc = pdu_meta->pdu->len;
142 pdu_meta->pdu->len += length;
143
144 isoal_test_debug_print_rx_pdu(pdu_meta);
145
146 return pdu_data_loc;
147 }
148
149 /**
150 * Create and fill in base information for a framed PDU
151 * @param[In] payload_number Payload number (Meta Information)
152 * @param[In] timestamp Adjusted RX time stamp (CIS anchorpoint)
153 * @param[In] status PDU error status
154 * @param[In/Out] pdu_meta PDU structure including meta information
155 */
isoal_test_create_framed_pdu_base(uint64_t payload_number,uint32_t timestamp,uint8_t status,struct isoal_pdu_rx * pdu_meta)156 void isoal_test_create_framed_pdu_base(uint64_t payload_number, uint32_t timestamp, uint8_t status,
157 struct isoal_pdu_rx *pdu_meta)
158 {
159 zassert_not_null(pdu_meta, "");
160 zassert_not_null(pdu_meta->meta, "");
161 zassert_not_null(pdu_meta->pdu, "");
162
163 memset(pdu_meta->meta, 0, sizeof(*pdu_meta->meta));
164 memset(pdu_meta->pdu, 0, sizeof(*pdu_meta->pdu));
165
166 pdu_meta->meta->payload_number = payload_number;
167 pdu_meta->meta->timestamp = timestamp;
168 pdu_meta->meta->status = status;
169
170 pdu_meta->pdu->ll_id = PDU_BIS_LLID_FRAMED;
171 pdu_meta->pdu->len = 0;
172
173 isoal_test_debug_print_rx_pdu(pdu_meta);
174 }
175
176 /**
177 * Adds a single SDU framed segment to the given PDU
178 * @param[In] dataptr Pointer to data to fill in segment
179 * @param[In] length Length of data
180 * @param[In] time_offset Time offset
181 * @param[In/Out] pdu_meta PDU structure including meta information
182 * @return PDU data location index
183 */
isoal_test_add_framed_pdu_single(uint8_t * dataptr,uint8_t length,uint32_t time_offset,struct isoal_pdu_rx * pdu_meta)184 uint16_t isoal_test_add_framed_pdu_single(uint8_t *dataptr, uint8_t length, uint32_t time_offset,
185 struct isoal_pdu_rx *pdu_meta)
186 {
187 zassert_not_null(pdu_meta, "");
188 zassert_not_null(pdu_meta->meta, "");
189 zassert_not_null(pdu_meta->pdu, "");
190
191 return isoal_test_insert_segment(false, true, time_offset, dataptr, length, pdu_meta);
192 }
193
194 /**
195 * Adds a starting SDU framed segment to the given PDU
196 * @param[In] dataptr Pointer to data to fill in segment
197 * @param[In] length Length of data
198 * @param[In] time_offset Time offset
199 * @param[In/Out] pdu_meta PDU structure including meta information
200 * @return PDU data location index
201 */
isoal_test_add_framed_pdu_start(uint8_t * dataptr,uint8_t length,uint32_t time_offset,struct isoal_pdu_rx * pdu_meta)202 uint16_t isoal_test_add_framed_pdu_start(uint8_t *dataptr, uint8_t length, uint32_t time_offset,
203 struct isoal_pdu_rx *pdu_meta)
204 {
205 zassert_not_null(pdu_meta, "");
206 zassert_not_null(pdu_meta->meta, "");
207 zassert_not_null(pdu_meta->pdu, "");
208
209 return isoal_test_insert_segment(false, false, time_offset, dataptr, length, pdu_meta);
210 }
211
212 /**
213 * Adds a continuation SDU framed segment to the given PDU
214 * @param[In] dataptr Pointer to data to fill in segment
215 * @param[In] length Length of data
216 * @param[In/Out] pdu_meta PDU structure including meta information
217 * @return PDU data location index
218 */
isoal_test_add_framed_pdu_cont(uint8_t * dataptr,uint8_t length,struct isoal_pdu_rx * pdu_meta)219 uint16_t isoal_test_add_framed_pdu_cont(uint8_t *dataptr,
220 uint8_t length,
221 struct isoal_pdu_rx *pdu_meta)
222 {
223 zassert_not_null(pdu_meta, "");
224 zassert_not_null(pdu_meta->meta, "");
225 zassert_not_null(pdu_meta->pdu, "");
226
227 return isoal_test_insert_segment(true, false, 0, dataptr, length, pdu_meta);
228 }
229
230 /**
231 * Adds an end SDU framed segment to the given PDU
232 * @param[In] dataptr Pointer to data to fill in segment
233 * @param[In] length Length of data
234 * @param[In/Out] pdu_meta PDU structure including meta information
235 * @return PDU data location index
236 */
isoal_test_add_framed_pdu_end(uint8_t * dataptr,uint8_t length,struct isoal_pdu_rx * pdu_meta)237 uint16_t isoal_test_add_framed_pdu_end(uint8_t *dataptr,
238 uint8_t length,
239 struct isoal_pdu_rx *pdu_meta)
240 {
241 zassert_not_null(pdu_meta, "");
242 zassert_not_null(pdu_meta->meta, "");
243 zassert_not_null(pdu_meta->pdu, "");
244
245 return isoal_test_insert_segment(true, true, 0, dataptr, length, pdu_meta);
246 }
247
248 /**
249 * Intializes a TX PDU buffer
250 * @param[in] buf Pointer to buffer structure
251 */
isoal_test_init_tx_pdu_buffer(struct tx_pdu_meta_buffer * buf)252 void isoal_test_init_tx_pdu_buffer(struct tx_pdu_meta_buffer *buf)
253 {
254 memset(buf, 0, sizeof(struct tx_pdu_meta_buffer));
255 }
256
257 /**
258 * Initializes a TX SDU buffer
259 * @param[in] buf Pointer to buffer structure
260 */
isoal_test_init_tx_sdu_buffer(struct tx_sdu_frag_buffer * buf)261 void isoal_test_init_tx_sdu_buffer(struct tx_sdu_frag_buffer *buf)
262 {
263 memset(buf, 0, sizeof(struct tx_sdu_frag_buffer));
264 buf->sdu_tx.dbuf = buf->sdu_payload;
265 }
266
267 /**
268 * Initialize the given test data buffer with a ramp pattern
269 * @param buf Test data buffer pointer
270 * @param size Length of test data
271 */
init_test_data_buffer(uint8_t * buf,uint16_t size)272 void init_test_data_buffer(uint8_t *buf, uint16_t size)
273 {
274 zassert_not_null(buf, "");
275
276 for (uint16_t i = 0; i < size; i++) {
277 buf[i] = (uint8_t)(i & 0x00FF);
278 }
279 }
280
281 /**
282 * @brief Wraps given time within the range of 0 to ULL_TIME_WRAPPING_POINT_US
283 * @param time_now Current time value
284 * @param time_diff Time difference (signed)
285 * @return Wrapped time after difference
286 */
ull_get_wrapped_time_us(uint32_t time_now_us,int32_t time_diff_us)287 uint32_t ull_get_wrapped_time_us(uint32_t time_now_us, int32_t time_diff_us)
288 {
289 uint32_t result = ((uint64_t)time_now_us + ULL_TIME_SPAN_FULL_US + time_diff_us) %
290 ((uint64_t)ULL_TIME_SPAN_FULL_US);
291
292 return result;
293 }
294