1 /*
2  * Copyright (c) 2021 Demant
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <string.h>
7 #include <stdlib.h>
8 
9 #include <zephyr/types.h>
10 #include <sys/types.h>
11 #include <zephyr/toolchain.h>
12 #include <zephyr/sys/util.h>
13 
14 #include <zephyr/kernel.h>
15 
16 #include <zephyr/bluetooth/hci_types.h>
17 #include <zephyr/bluetooth/conn.h>
18 
19 #include "util/memq.h"
20 
21 #include "hal/ccm.h"
22 #include "hal/ticker.h"
23 
24 #include "pdu_df.h"
25 #include "lll/pdu_vendor.h"
26 #include "pdu.h"
27 
28 #include "ll.h"
29 #include "lll.h"
30 #include "lll_conn_iso.h"
31 #include "lll_iso_tx.h"
32 #include "isoal.h"
33 #include "ull_iso_types.h"
34 
35 #include <zephyr/logging/log.h>
36 
37 LOG_MODULE_REGISTER(bt_ctlr_isoal, LOG_LEVEL_INF);
38 
39 #define ISOAL_LOG_DBG(...)     LOG_DBG(__VA_ARGS__)
40 
41 #if defined(ISOAL_DEBUG_VERBOSE)
42 #define ISOAL_LOG_DBGV(...)    LOG_DBG(__VA_ARGS__)
43 #else
44 #define ISOAL_LOG_DBGV(...)    (void) 0
45 #endif /* ISOAL_DEBUG_VERBOSE */
46 
47 #include "hal/debug.h"
48 
49 #define FSM_TO_STR(s) (s == ISOAL_START ? "START" : \
50 	(s == ISOAL_CONTINUE ? "CONTINUE" : \
51 		(s == ISOAL_ERR_SPOOL ? "ERR SPOOL" : "???")))
52 
53 #if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
54 /* Given the minimum payload, this defines the minimum number of bytes that
55  * should be  remaining in a TX PDU such that it would make inserting a new
56  * segment worthwhile during the segmentation process.
57  * [Payload (min) + Segmentation Header + Time Offset]
58  */
59 #define ISOAL_TX_SEGMENT_MIN_SIZE         (CONFIG_BT_CTLR_ISO_TX_SEG_PLAYLOAD_MIN +                \
60 					   PDU_ISO_SEG_HDR_SIZE +                                  \
61 					   PDU_ISO_SEG_TIMEOFFSET_SIZE)
62 #endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
63 
64 /* Defined the wrapping point and mid point in the range of time input values,
65  * which depend on range of the controller's clock in microseconds.
66  */
67 #define ISOAL_TIME_WRAPPING_POINT_US      (HAL_TICKER_TICKS_TO_US(HAL_TICKER_CNTR_MASK))
68 #define ISOAL_TIME_MID_POINT_US           (ISOAL_TIME_WRAPPING_POINT_US / 2)
69 #define ISOAL_TIME_SPAN_FULL_US           (ISOAL_TIME_WRAPPING_POINT_US + 1)
70 #define ISOAL_TIME_SPAN_HALF_US           (ISOAL_TIME_SPAN_FULL_US / 2)
71 
72 /** Allocation state */
73 typedef uint8_t isoal_alloc_state_t;
74 #define ISOAL_ALLOC_STATE_FREE            ((isoal_alloc_state_t) 0x00)
75 #define ISOAL_ALLOC_STATE_TAKEN           ((isoal_alloc_state_t) 0x01)
76 
77 struct
78 {
79 #if defined(CONFIG_BT_CTLR_SYNC_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
80 	isoal_alloc_state_t sink_allocated[CONFIG_BT_CTLR_ISOAL_SINKS];
81 	struct isoal_sink   sink_state[CONFIG_BT_CTLR_ISOAL_SINKS];
82 #endif /* CONFIG_BT_CTLR_SYNC_ISO || CONFIG_BT_CTLR_CONN_ISO */
83 
84 #if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
85 	isoal_alloc_state_t source_allocated[CONFIG_BT_CTLR_ISOAL_SOURCES];
86 	struct isoal_source source_state[CONFIG_BT_CTLR_ISOAL_SOURCES];
87 #endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
88 } isoal_global;
89 
90 /**
91  * @brief Internal reset
92  * Zero-init entire ISO-AL state
93  */
isoal_init_reset(void)94 static isoal_status_t isoal_init_reset(void)
95 {
96 	memset(&isoal_global, 0, sizeof(isoal_global));
97 	return ISOAL_STATUS_OK;
98 }
99 
100 /**
101  * @brief  Initialize ISO-AL
102  */
isoal_init(void)103 isoal_status_t isoal_init(void)
104 {
105 	isoal_status_t err = ISOAL_STATUS_OK;
106 
107 	err = isoal_init_reset();
108 
109 	return err;
110 }
111 
112 /** Clean up and reinitialize */
isoal_reset(void)113 isoal_status_t isoal_reset(void)
114 {
115 	isoal_status_t err = ISOAL_STATUS_OK;
116 
117 	err = isoal_init_reset();
118 
119 	return err;
120 }
121 
122 /**
123  * @brief Wraps given time within the range of 0 to ISOAL_TIME_WRAPPING_POINT_US
124  * @param  time_now  Current time value
125  * @param  time_diff Time difference (signed)
126  * @return           Wrapped time after difference
127  */
isoal_get_wrapped_time_us(uint32_t time_now_us,int32_t time_diff_us)128 uint32_t isoal_get_wrapped_time_us(uint32_t time_now_us, int32_t time_diff_us)
129 {
130 	LL_ASSERT(time_now_us <= ISOAL_TIME_WRAPPING_POINT_US);
131 
132 	uint32_t result = ((uint64_t)time_now_us + ISOAL_TIME_SPAN_FULL_US + time_diff_us) %
133 				((uint64_t)ISOAL_TIME_SPAN_FULL_US);
134 
135 	return result;
136 }
137 
138 /**
139  * @brief Check if a time difference calculation is valid and return the difference.
140  * @param  time_before Subtrahend
141  * @param  time_after  Minuend
142  * @param  result      Difference if valid
143  * @return             Validity - valid if time_after leads time_before with
144  *                                consideration for wrapping such that the
145  *                                difference can be calculated.
146  */
isoal_get_time_diff(uint32_t time_before,uint32_t time_after,uint32_t * result)147 static bool isoal_get_time_diff(uint32_t time_before, uint32_t time_after, uint32_t *result)
148 {
149 	bool valid = false;
150 
151 	LL_ASSERT(time_before <= ISOAL_TIME_WRAPPING_POINT_US);
152 	LL_ASSERT(time_after <= ISOAL_TIME_WRAPPING_POINT_US);
153 
154 	if (time_before > time_after) {
155 		if (time_before >= ISOAL_TIME_MID_POINT_US &&
156 			time_after <= ISOAL_TIME_MID_POINT_US) {
157 			if ((time_before - time_after) <=  ISOAL_TIME_SPAN_HALF_US) {
158 				/* Time_before is after time_after and the result is invalid. */
159 			} else {
160 				/* time_after has wrapped */
161 				*result = time_after + ISOAL_TIME_SPAN_FULL_US - time_before;
162 				valid = true;
163 			}
164 		}
165 
166 		/* Time_before is after time_after and the result is invalid. */
167 	} else {
168 		/* Time_before <= time_after */
169 		*result = time_after - time_before;
170 		if (*result <=  ISOAL_TIME_SPAN_HALF_US) {
171 			/* result is valid  if it is within half the maximum
172 			 * time span.
173 			 */
174 			valid = true;
175 		} else {
176 			/* time_before has wrapped and the calculation is not
177 			 * valid as time_before is ahead of time_after.
178 			 */
179 		}
180 	}
181 
182 	return valid;
183 }
184 
185 #if defined(CONFIG_BT_CTLR_SYNC_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
186 
187 #define SET_RX_SDU_TIMESTAMP(_sink, _timestamp, _value)                        \
188 	_timestamp = _value;                                                   \
189 	ISOAL_LOG_DBGV("[%p] %s updated (%ld)", _sink, #_timestamp, _value);
190 
191 static void isoal_rx_framed_update_sdu_release(struct isoal_sink *sink);
192 
193 /**
194  * @brief Find free sink from statically-sized pool and allocate it
195  * @details Implemented as linear search since pool is very small
196  *
197  * @param hdl[out]  Handle to sink
198  * @return ISOAL_STATUS_OK if we could allocate; otherwise ISOAL_STATUS_ERR_SINK_ALLOC
199  */
isoal_sink_allocate(isoal_sink_handle_t * hdl)200 static isoal_status_t isoal_sink_allocate(isoal_sink_handle_t *hdl)
201 {
202 	isoal_sink_handle_t i;
203 
204 	/* Very small linear search to find first free */
205 	for (i = 0; i < CONFIG_BT_CTLR_ISOAL_SINKS; i++) {
206 		if (isoal_global.sink_allocated[i] == ISOAL_ALLOC_STATE_FREE) {
207 			isoal_global.sink_allocated[i] = ISOAL_ALLOC_STATE_TAKEN;
208 			*hdl = i;
209 			return ISOAL_STATUS_OK;
210 		}
211 	}
212 
213 	return ISOAL_STATUS_ERR_SINK_ALLOC; /* All entries were taken */
214 }
215 
216 /**
217  * @brief Mark a sink as being free to allocate again
218  * @param hdl[in]  Handle to sink
219  */
isoal_sink_deallocate(isoal_sink_handle_t hdl)220 static void isoal_sink_deallocate(isoal_sink_handle_t hdl)
221 {
222 	isoal_global.sink_allocated[hdl] = ISOAL_ALLOC_STATE_FREE;
223 	(void)memset(&isoal_global.sink_state[hdl], 0, sizeof(struct isoal_sink));
224 }
225 
226 /**
227  * @brief Create a new sink
228  *
229  * @param handle[in]            Connection handle
230  * @param role[in]              Peripheral, Central or Broadcast
231  * @param framed[in]            Framed case
232  * @param burst_number[in]      Burst Number
233  * @param flush_timeout[in]     Flush timeout
234  * @param sdu_interval[in]      SDU interval
235  * @param iso_interval[in]      ISO interval
236  * @param stream_sync_delay[in] CIS / BIS sync delay
237  * @param group_sync_delay[in]  CIG / BIG sync delay
238  * @param sdu_alloc[in]         Callback of SDU allocator
239  * @param sdu_emit[in]          Callback of SDU emitter
240  * @param sdu_write[in]         Callback of SDU byte writer
241  * @param hdl[out]              Handle to new sink
242  *
243  * @return ISOAL_STATUS_OK if we could create a new sink; otherwise ISOAL_STATUS_ERR_SINK_ALLOC
244  */
isoal_sink_create(uint16_t handle,uint8_t role,uint8_t framed,uint8_t burst_number,uint8_t flush_timeout,uint32_t sdu_interval,uint16_t iso_interval,uint32_t stream_sync_delay,uint32_t group_sync_delay,isoal_sink_sdu_alloc_cb sdu_alloc,isoal_sink_sdu_emit_cb sdu_emit,isoal_sink_sdu_write_cb sdu_write,isoal_sink_handle_t * hdl)245 isoal_status_t isoal_sink_create(
246 	uint16_t                 handle,
247 	uint8_t                  role,
248 	uint8_t                  framed,
249 	uint8_t                  burst_number,
250 	uint8_t                  flush_timeout,
251 	uint32_t                 sdu_interval,
252 	uint16_t                 iso_interval,
253 	uint32_t                 stream_sync_delay,
254 	uint32_t                 group_sync_delay,
255 	isoal_sink_sdu_alloc_cb  sdu_alloc,
256 	isoal_sink_sdu_emit_cb   sdu_emit,
257 	isoal_sink_sdu_write_cb  sdu_write,
258 	isoal_sink_handle_t      *hdl)
259 {
260 	uint32_t iso_interval_us;
261 	isoal_status_t err;
262 
263 	/* ISO interval in units of time requires the integer (iso_interval)
264 	 * to be multiplied by 1250us.
265 	 */
266 	iso_interval_us = iso_interval * ISO_INT_UNIT_US;
267 
268 	/* Allocate a new sink */
269 	err = isoal_sink_allocate(hdl);
270 	if (err) {
271 		return err;
272 	}
273 
274 	struct isoal_sink_session *session = &isoal_global.sink_state[*hdl].session;
275 
276 	session->handle = handle;
277 	session->framed = framed;
278 	session->sdu_interval = sdu_interval;
279 	session->iso_interval = iso_interval;
280 	session->burst_number = burst_number;
281 
282 	/* Todo: Next section computing various constants, should potentially be a
283 	 * function in itself as a number of the dependencies could be changed while
284 	 * a connection is active.
285 	 */
286 
287 	session->pdus_per_sdu = (burst_number * sdu_interval) /
288 		iso_interval_us;
289 
290 	/* Computation of transport latency (constant part)
291 	 *
292 	 * Unframed case:
293 	 *
294 	 * C->P: SDU_Synchronization_Reference =
295 	 *   CIS reference anchor point + CIS_Sync_Delay + (FT_C_To_P - 1) * ISO_Interval
296 	 *
297 	 * P->C: SDU_Synchronization_Reference =
298 	 *   CIS reference anchor point + CIS_Sync_Delay - CIG_Sync_Delay -
299 	 *   ((ISO_Interval / SDU interval)-1) * SDU interval
300 	 *
301 	 * BIS: SDU_Synchronization_Reference =
302 	 *    BIG reference anchor point + BIG_Sync_Delay
303 	 *
304 	 * Framed case:
305 	 *
306 	 * C->P: SDU_Synchronization_Reference =
307 	 *   CIS Reference Anchor point +
308 	 *   CIS_Sync_Delay + SDU_Interval_C_To_P + FT_C_To_P * ISO_Interval -
309 	 *   Time_Offset
310 	 *
311 	 * P->C: synchronization reference SDU = CIS reference anchor point +
312 	 *   CIS_Sync_Delay - CIG_Sync_Delay - Time_Offset
313 	 *
314 	 * BIS: SDU_Synchronization_Reference =
315 	 *   BIG reference anchor point +
316 	 *   BIG_Sync_Delay + SDU_interval + ISO_Interval - Time_Offset.
317 	 */
318 	if (role == ISOAL_ROLE_PERIPHERAL) {
319 		if (framed) {
320 			session->sdu_sync_const = stream_sync_delay + sdu_interval +
321 							(flush_timeout * iso_interval_us);
322 		} else {
323 			session->sdu_sync_const = stream_sync_delay +
324 							((flush_timeout - 1UL) * iso_interval_us);
325 		}
326 	} else if (role == ISOAL_ROLE_CENTRAL) {
327 		if (framed) {
328 			session->sdu_sync_const = stream_sync_delay - group_sync_delay;
329 		} else {
330 			session->sdu_sync_const = stream_sync_delay - group_sync_delay -
331 							(((iso_interval_us / sdu_interval) - 1UL) *
332 								iso_interval_us);
333 		}
334 	} else if (role == ISOAL_ROLE_BROADCAST_SINK) {
335 		if (framed) {
336 			session->sdu_sync_const = group_sync_delay + sdu_interval + iso_interval_us;
337 		} else {
338 			session->sdu_sync_const = group_sync_delay;
339 		}
340 	} else {
341 		LL_ASSERT(0);
342 	}
343 
344 	/* Remember the platform-specific callbacks */
345 	session->sdu_alloc = sdu_alloc;
346 	session->sdu_emit  = sdu_emit;
347 	session->sdu_write = sdu_write;
348 
349 	/* Initialize running seq number to zero */
350 	session->sn = 0;
351 
352 	return err;
353 }
354 
355 /**
356  * @brief Atomically enable latch-in of packets and SDU production
357  * @param hdl[in]  Handle of existing instance
358  */
isoal_sink_enable(isoal_sink_handle_t hdl)359 void isoal_sink_enable(isoal_sink_handle_t hdl)
360 {
361 	/* Reset bookkeeping state */
362 	memset(&isoal_global.sink_state[hdl].sdu_production, 0,
363 	       sizeof(isoal_global.sink_state[hdl].sdu_production));
364 
365 	/* Atomically enable */
366 	isoal_global.sink_state[hdl].sdu_production.mode = ISOAL_PRODUCTION_MODE_ENABLED;
367 }
368 
369 /**
370  * @brief Atomically disable latch-in of packets and SDU production
371  * @param hdl[in]  Handle of existing instance
372  */
isoal_sink_disable(isoal_sink_handle_t hdl)373 void isoal_sink_disable(isoal_sink_handle_t hdl)
374 {
375 	/* Atomically disable */
376 	isoal_global.sink_state[hdl].sdu_production.mode = ISOAL_PRODUCTION_MODE_DISABLED;
377 }
378 
379 /**
380  * @brief Disable and deallocate existing sink
381  * @param hdl[in]  Handle of existing instance
382  */
isoal_sink_destroy(isoal_sink_handle_t hdl)383 void isoal_sink_destroy(isoal_sink_handle_t hdl)
384 {
385 	/* Atomic disable */
386 	isoal_sink_disable(hdl);
387 
388 	/* Permit allocation anew */
389 	isoal_sink_deallocate(hdl);
390 }
391 
392 /* Obtain destination SDU */
isoal_rx_allocate_sdu(struct isoal_sink * sink,const struct isoal_pdu_rx * pdu_meta)393 static isoal_status_t isoal_rx_allocate_sdu(struct isoal_sink *sink,
394 					    const struct isoal_pdu_rx *pdu_meta)
395 {
396 	struct isoal_sink_session *session;
397 	struct isoal_sdu_production *sp;
398 	struct isoal_sdu_produced *sdu;
399 	isoal_status_t err;
400 
401 	err = ISOAL_STATUS_OK;
402 	session = &sink->session;
403 	sp = &sink->sdu_production;
404 	sdu = &sp->sdu;
405 
406 	/* Allocate a SDU if the previous was filled (thus sent) */
407 	const bool sdu_complete = (sp->sdu_available == 0);
408 
409 	if (sdu_complete) {
410 		/* Allocate new clean SDU buffer */
411 		err = session->sdu_alloc(
412 			sink,
413 			pdu_meta,      /* [in]  PDU origin may determine buffer */
414 			&sdu->contents  /* [out] Updated with pointer and size */
415 		);
416 
417 		if (err == ISOAL_STATUS_OK) {
418 			sp->sdu_allocated = true;
419 		}
420 
421 		/* Nothing has been written into buffer yet */
422 		sp->sdu_written   = 0;
423 		sp->sdu_available = sdu->contents.size;
424 		LL_ASSERT(sdu->contents.size > 0);
425 
426 		/* Get seq number from session counter */
427 		sdu->sn = session->sn;
428 	}
429 
430 	return err;
431 }
432 
433 /**
434  * @brief  Depending of whether the configuration is enabled, this will either
435  *         buffer and collate information for the SDU across all fragments
436  *         before emitting the batch of fragments, or immediately release the
437  *         fragment.
438  * @param  sink       Point to the sink context structure
439  * @param  end_of_sdu Indicates if this is the end fragment of an SDU or forced
440  *                    release on an error
441  * @return            Status of operation
442  */
isoal_rx_buffered_emit_sdu(struct isoal_sink * sink,bool end_of_sdu)443 static isoal_status_t isoal_rx_buffered_emit_sdu(struct isoal_sink *sink, bool end_of_sdu)
444 {
445 	struct isoal_emitted_sdu_frag sdu_frag;
446 	struct isoal_emitted_sdu sdu_status;
447 	struct isoal_sink_session *session;
448 	struct isoal_sdu_production *sp;
449 	struct isoal_sdu_produced *sdu;
450 	bool emit_sdu_current;
451 	isoal_status_t err;
452 
453 	err = ISOAL_STATUS_OK;
454 	session = &sink->session;
455 	sp = &sink->sdu_production;
456 	sdu = &sp->sdu;
457 
458 	/* Initialize current SDU fragment buffer */
459 	sdu_frag.sdu_state = sp->sdu_state;
460 	sdu_frag.sdu_frag_size = sp->sdu_written;
461 	sdu_frag.sdu = *sdu;
462 
463 	sdu_status.total_sdu_size = sdu_frag.sdu_frag_size;
464 	sdu_status.collated_status = sdu_frag.sdu.status;
465 	emit_sdu_current = true;
466 
467 #if defined(ISOAL_BUFFER_RX_SDUS_ENABLE)
468 	uint16_t next_write_indx;
469 	bool sdu_list_empty;
470 	bool emit_sdu_list;
471 	bool sdu_list_max;
472 	bool sdu_list_err;
473 
474 	next_write_indx = sp->sdu_list.next_write_indx;
475 	sdu_list_max = (next_write_indx >= CONFIG_BT_CTLR_ISO_RX_SDU_BUFFERS);
476 	sdu_list_empty = (next_write_indx == 0);
477 
478 	/* There is an error in the sequence of SDUs if the current SDU fragment
479 	 * is not an end fragment and either the list at capacity or the current
480 	 * fragment is not a continuation (i.e. it is a start of a new SDU).
481 	 */
482 	sdu_list_err = !end_of_sdu &&
483 		(sdu_list_max ||
484 		(!sdu_list_empty && sdu_frag.sdu_state != BT_ISO_CONT));
485 
486 	/* Release the current fragment if it is the end of the SDU or if it is
487 	 * not the starting fragment of a multi-fragment SDU.
488 	 */
489 	emit_sdu_current = end_of_sdu || (sdu_list_empty && sdu_frag.sdu_state != BT_ISO_START);
490 
491 	/* Flush the buffered SDUs if this is an end fragment either on account
492 	 * of reaching the end of the SDU or on account of an error or if
493 	 * there is an error in the sequence of buffered fragments.
494 	 */
495 	emit_sdu_list = emit_sdu_current || sdu_list_err;
496 
497 	/* Total size is cleared if the current fragment is not being emitted
498 	 * or if there is an error in the sequence of fragments.
499 	 */
500 	if (!emit_sdu_current || sdu_list_err) {
501 		sdu_status.total_sdu_size = 0;
502 		sdu_status.collated_status = (sdu_list_err ? ISOAL_SDU_STATUS_LOST_DATA :
503 						ISOAL_SDU_STATUS_VALID);
504 	}
505 
506 	if (emit_sdu_list && next_write_indx > 0) {
507 		if (!sdu_list_err) {
508 			/* Collated information is not reliable if there is an
509 			 * error in the sequence of the fragments.
510 			 */
511 			for (uint8_t i = 0; i < next_write_indx; i++) {
512 				sdu_status.total_sdu_size +=
513 					sp->sdu_list.list[i].sdu_frag_size;
514 				if (sp->sdu_list.list[i].sdu.status == ISOAL_SDU_STATUS_LOST_DATA ||
515 					sdu_status.collated_status == ISOAL_SDU_STATUS_LOST_DATA) {
516 					sdu_status.collated_status = ISOAL_SDU_STATUS_LOST_DATA;
517 				} else {
518 					sdu_status.collated_status |=
519 						sp->sdu_list.list[i].sdu.status;
520 				}
521 			}
522 		}
523 
524 		for (uint8_t i = 0; i < next_write_indx; i++) {
525 			err |= session->sdu_emit(sink, &sp->sdu_list.list[i],
526 						&sdu_status);
527 		}
528 
529 		next_write_indx = sp->sdu_list.next_write_indx  = 0;
530 	}
531 #endif /* ISOAL_BUFFER_RX_SDUS_ENABLE */
532 
533 	if (emit_sdu_current) {
534 		if (sdu_frag.sdu_state == BT_ISO_SINGLE) {
535 			sdu_status.total_sdu_size = sdu_frag.sdu_frag_size;
536 			sdu_status.collated_status = sdu_frag.sdu.status;
537 		}
538 
539 		ISOAL_LOG_DBG("[%p] SDU %u @TS=%u err=%X len=%u released\n",
540 			      sink, sdu_frag.sdu.sn, sdu_frag.sdu.timestamp,
541 			      sdu_status.collated_status, sdu_status.total_sdu_size);
542 		err |= session->sdu_emit(sink, &sdu_frag, &sdu_status);
543 
544 #if defined(ISOAL_BUFFER_RX_SDUS_ENABLE)
545 	} else if (next_write_indx < CONFIG_BT_CTLR_ISO_RX_SDU_BUFFERS) {
546 		sp->sdu_list.list[next_write_indx++] = sdu_frag;
547 		sp->sdu_list.next_write_indx = next_write_indx;
548 #endif /* ISOAL_BUFFER_RX_SDUS_ENABLE */
549 	} else {
550 		/* Unreachable */
551 		LL_ASSERT(0);
552 	}
553 
554 	return err;
555 }
556 
isoal_rx_try_emit_sdu(struct isoal_sink * sink,bool end_of_sdu)557 static isoal_status_t isoal_rx_try_emit_sdu(struct isoal_sink *sink, bool end_of_sdu)
558 {
559 	struct isoal_sink_session *session;
560 	struct isoal_sdu_production *sp;
561 	struct isoal_sdu_produced *sdu;
562 	isoal_status_t err;
563 
564 	err = ISOAL_STATUS_OK;
565 	sp = &sink->sdu_production;
566 	session = &sink->session;
567 	sdu = &sp->sdu;
568 
569 	/* Emit a SDU */
570 	const bool sdu_complete = (sp->sdu_available == 0) || end_of_sdu;
571 
572 	if (end_of_sdu) {
573 		sp->sdu_available = 0;
574 	}
575 
576 	if (sdu_complete) {
577 		uint8_t next_state = BT_ISO_START;
578 
579 		switch (sp->sdu_state) {
580 		case BT_ISO_START:
581 			if (end_of_sdu) {
582 				sp->sdu_state = BT_ISO_SINGLE;
583 				next_state = BT_ISO_START;
584 			} else {
585 				sp->sdu_state = BT_ISO_START;
586 				next_state = BT_ISO_CONT;
587 			}
588 			break;
589 		case BT_ISO_CONT:
590 			if (end_of_sdu) {
591 				sp->sdu_state  = BT_ISO_END;
592 				next_state = BT_ISO_START;
593 			} else {
594 				sp->sdu_state  = BT_ISO_CONT;
595 				next_state = BT_ISO_CONT;
596 			}
597 			break;
598 		}
599 		sdu->status = sp->sdu_status;
600 
601 		err = isoal_rx_buffered_emit_sdu(sink, end_of_sdu);
602 		sp->sdu_allocated = false;
603 
604 		if (end_of_sdu) {
605 			isoal_rx_framed_update_sdu_release(sink);
606 			sp->sdu_status = ISOAL_SDU_STATUS_VALID;
607 			session->sn++;
608 		}
609 
610 		/* update next state */
611 		sink->sdu_production.sdu_state = next_state;
612 	}
613 
614 	return err;
615 }
616 
isoal_rx_append_to_sdu(struct isoal_sink * sink,const struct isoal_pdu_rx * pdu_meta,uint8_t offset,uint8_t length,bool is_end_fragment,bool is_padding)617 static isoal_status_t isoal_rx_append_to_sdu(struct isoal_sink *sink,
618 					     const struct isoal_pdu_rx *pdu_meta,
619 					     uint8_t offset,
620 					     uint8_t length,
621 					     bool is_end_fragment,
622 					     bool is_padding)
623 {
624 	isoal_pdu_len_t packet_available;
625 	const uint8_t *pdu_payload;
626 	bool handle_error_case;
627 	isoal_status_t err;
628 
629 	/* Might get an empty packed due to errors, we will need to terminate
630 	 * and send something up anyhow
631 	 */
632 	packet_available = length;
633 	handle_error_case = (is_end_fragment && (packet_available == 0));
634 
635 	pdu_payload = pdu_meta->pdu->payload + offset;
636 	LL_ASSERT(pdu_payload);
637 
638 	/* While there is something left of the packet to consume */
639 	err = ISOAL_STATUS_OK;
640 	while ((packet_available > 0) || handle_error_case) {
641 		isoal_status_t err_alloc;
642 		struct isoal_sdu_production *sp;
643 		struct isoal_sdu_produced *sdu;
644 
645 		err_alloc = ISOAL_STATUS_OK;
646 		if (!is_padding) {
647 			/* A new SDU should only be allocated if the current is
648 			 * not padding. Covers a situation where the end
649 			 * fragment was not received.
650 			 */
651 			err_alloc = isoal_rx_allocate_sdu(sink, pdu_meta);
652 		}
653 
654 		sp = &sink->sdu_production;
655 		sdu = &sp->sdu;
656 
657 		err |= err_alloc;
658 
659 		/*
660 		 * For this SDU we can only consume of packet, bounded by:
661 		 *   - What can fit in the destination SDU.
662 		 *   - What remains of the packet.
663 		 */
664 		const size_t consume_len = MIN(
665 			packet_available,
666 			sp->sdu_available
667 		);
668 
669 		if (consume_len > 0) {
670 			const struct isoal_sink_session *session = &sink->session;
671 
672 			err |= session->sdu_write(sdu->contents.dbuf,
673 						  pdu_payload,
674 						  consume_len);
675 			pdu_payload += consume_len;
676 			sp->sdu_written   += consume_len;
677 			sp->sdu_available -= consume_len;
678 			packet_available  -= consume_len;
679 		}
680 		bool end_of_sdu = (packet_available == 0) && is_end_fragment;
681 
682 		isoal_status_t err_emit = ISOAL_STATUS_OK;
683 
684 		if (sp->sdu_allocated) {
685 			/* SDU should be emitted only if it was allocated */
686 			err_emit = isoal_rx_try_emit_sdu(sink, end_of_sdu);
687 		}
688 
689 		handle_error_case = false;
690 		err |= err_emit;
691 	}
692 
693 	return err;
694 }
695 
696 
697 /**
698  * @brief Consume an unframed PDU: Copy contents into SDU(s) and emit to a sink
699  * @details Destination sink may have an already partially built SDU
700  *
701  * @param sink[in,out]    Destination sink with bookkeeping state
702  * @param pdu_meta[out]  PDU with meta information (origin, timing, status)
703  *
704  * @return Status
705  */
isoal_rx_unframed_consume(struct isoal_sink * sink,const struct isoal_pdu_rx * pdu_meta)706 static isoal_status_t isoal_rx_unframed_consume(struct isoal_sink *sink,
707 						const struct isoal_pdu_rx *pdu_meta)
708 {
709 	struct isoal_sink_session *session;
710 	struct isoal_sdu_production *sp;
711 	struct node_rx_iso_meta *meta;
712 	struct pdu_iso *pdu;
713 	bool end_of_packet;
714 	uint8_t next_state;
715 	isoal_status_t err;
716 	bool pdu_padding;
717 	uint8_t length;
718 	bool last_pdu;
719 	bool pdu_err;
720 	bool seq_err;
721 	uint8_t llid;
722 
723 	sp = &sink->sdu_production;
724 	session = &sink->session;
725 	meta = pdu_meta->meta;
726 	pdu = pdu_meta->pdu;
727 
728 	err = ISOAL_STATUS_OK;
729 	next_state = ISOAL_START;
730 
731 	/* If status is not ISOAL_PDU_STATUS_VALID, length and LLID cannot be trusted */
732 	llid = pdu_meta->pdu->ll_id;
733 	pdu_err = (pdu_meta->meta->status != ISOAL_PDU_STATUS_VALID);
734 	length = pdu_meta->pdu->len;
735 	/* A zero length PDU with LLID 0b01 (PDU_BIS_LLID_START_CONTINUE) would be a padding PDU.
736 	 * However if there are errors in the PDU, it could be an incorrectly receive non-padding
737 	 * PDU. Therefore only consider a PDU with errors as padding if received after the end
738 	 * fragment is seen when padding PDUs are expected.
739 	 */
740 	pdu_padding = (length == 0) && (llid == PDU_BIS_LLID_START_CONTINUE) &&
741 		      (!pdu_err || sp->fsm == ISOAL_ERR_SPOOL);
742 	seq_err = (meta->payload_number != (sp->prev_pdu_id+1));
743 
744 	/* If there are no buffers available, the PDUs received by the ISO-AL
745 	 * may not be in sequence even though this is expected for unframed rx.
746 	 * It would be necessary to exit the ISOAL_ERR_SPOOL state as the PDU
747 	 * count and as a result the last_pdu detection is no longer reliable.
748 	 */
749 	if (sp->fsm == ISOAL_ERR_SPOOL) {
750 		if ((!pdu_err && !seq_err &&
751 			/* Previous sequence error should have move to the
752 			 * ISOAL_ERR_SPOOL state and emitted the SDU in production. No
753 			 * PDU error so LLID and length are reliable and no sequence
754 			 * error so this PDU is the next in order.
755 			 */
756 			((sp->prev_pdu_is_end || sp->prev_pdu_is_padding) &&
757 				((llid == PDU_BIS_LLID_START_CONTINUE && length > 0) ||
758 					(llid == PDU_BIS_LLID_COMPLETE_END && length == 0))))
759 			/* Detected a start of a new SDU as the last PDU was an end
760 			 * fragment or padding and the current is the start of a new SDU
761 			 * (either filled or zero length). Move to ISOAL_START
762 			 * immediately.
763 			 */
764 
765 			|| (meta->payload_number % session->pdus_per_sdu == 0)) {
766 			/* Based on the payload number, this should be the start
767 			 * of a new SDU.
768 			 */
769 			sp->fsm = ISOAL_START;
770 		}
771 	}
772 
773 	if (sp->fsm == ISOAL_START) {
774 		struct isoal_sdu_produced *sdu;
775 		uint32_t anchorpoint;
776 		uint16_t sdu_offset;
777 		int32_t latency;
778 
779 		sp->sdu_status = ISOAL_SDU_STATUS_VALID;
780 		sp->sdu_state = BT_ISO_START;
781 		sp->pdu_cnt = 1;
782 		sp->only_padding = pdu_padding;
783 		seq_err = false;
784 
785 		/* The incoming time stamp for each PDU is expected to be the
786 		 * CIS / BIS reference anchor point. SDU reference point is
787 		 * reconstructed by adding the precalculated latency constant.
788 		 *
789 		 * BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
790 		 * 3.2.2 SDU synchronization reference using unframed PDUs:
791 		 *
792 		 * The CIS reference anchor point is computed excluding any
793 		 * retransmissions or missed subevents and shall be set to the
794 		 * start of the isochronous event in which the first PDU
795 		 * containing the SDU could have been transferred.
796 		 *
797 		 * The BIG reference anchor point is the anchor point of the BIG
798 		 * event that the PDU is associated with.
799 		 */
800 		anchorpoint = meta->timestamp;
801 		latency = session->sdu_sync_const;
802 		sdu = &sp->sdu;
803 		sdu->timestamp = isoal_get_wrapped_time_us(anchorpoint, latency);
804 
805 		/* If there are multiple SDUs in an ISO interval
806 		 * (SDU interval < ISO Interval) every SDU after the first
807 		 * should add an SDU interval to the time stamp.
808 		 *
809 		 * BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
810 		 * 3.2.2 SDU synchronization reference using unframed PDUs:
811 		 *
812 		 * All PDUs belonging to a burst as defined by the configuration
813 		 * of BN have the same reference anchor point. When multiple
814 		 * SDUs have the same reference anchor point, the first SDU uses
815 		 * the reference anchor point timing. Each subsequent SDU
816 		 * increases the SDU synchronization reference timing with one
817 		 * SDU interval.
818 		 */
819 		sdu_offset = (meta->payload_number % session->burst_number) / session->pdus_per_sdu;
820 		sdu->timestamp = isoal_get_wrapped_time_us(sdu->timestamp,
821 					sdu_offset * session->sdu_interval);
822 	} else {
823 		sp->pdu_cnt++;
824 	}
825 
826 	last_pdu = (sp->pdu_cnt == session->pdus_per_sdu);
827 	end_of_packet = (llid == PDU_BIS_LLID_COMPLETE_END) || last_pdu || pdu_err;
828 	sp->only_padding = sp->only_padding && pdu_padding;
829 
830 	switch (sp->fsm) {
831 	case ISOAL_START:
832 	case ISOAL_CONTINUE:
833 		if (pdu_err || seq_err) {
834 			/* PDU contains errors */
835 			if (last_pdu) {
836 				/* Last PDU all done */
837 				next_state = ISOAL_START;
838 			} else {
839 				next_state = ISOAL_ERR_SPOOL;
840 			}
841 		} else if (llid == PDU_BIS_LLID_START_CONTINUE) {
842 			/* PDU contains a continuation (neither start of end) fragment of SDU */
843 			if (last_pdu) {
844 				/* last pdu in sdu, but end fragment not seen, emit with error */
845 				next_state = ISOAL_START;
846 			} else {
847 				next_state = ISOAL_CONTINUE;
848 			}
849 		} else if (llid == PDU_BIS_LLID_COMPLETE_END) {
850 			/* PDU contains end fragment of a fragmented SDU */
851 			if (last_pdu) {
852 				/* Last PDU all done */
853 				next_state = ISOAL_START;
854 			} else {
855 				/* Padding after end fragment to follow */
856 				next_state = ISOAL_ERR_SPOOL;
857 			}
858 		} else  {
859 			/* Unsupported case */
860 			err = ISOAL_STATUS_ERR_UNSPECIFIED;
861 			LOG_ERR("Invalid unframed LLID (%d)", llid);
862 			LL_ASSERT(0);
863 		}
864 		break;
865 
866 	case ISOAL_ERR_SPOOL:
867 		/* State assumes that at end fragment or err has been seen,
868 		 * now just consume the rest
869 		 */
870 		if (last_pdu) {
871 			/* Last padding seen, restart */
872 			next_state = ISOAL_START;
873 		} else {
874 			next_state = ISOAL_ERR_SPOOL;
875 		}
876 		break;
877 
878 	}
879 
880 	/* Update error state */
881 	/* Prioritisation:
882 	 * (1) Sequence Error should set the ISOAL_SDU_STATUS_LOST_DATA status
883 	 *     as data is missing and this will trigger the HCI to discard any
884 	 *     data received.
885 	 *
886 	 *     BT Core V5.3 : Vol 4 HCI I/F : Part G HCI Func. Spec.:
887 	 *     5.4.5 HCI ISO Data packets
888 	 *     If Packet_Status_Flag equals 0b10 then PB_Flag shall equal 0b10.
889 	 *     When Packet_Status_Flag is set to 0b10 in packets from the Controller to the
890 	 *     Host, there is no data and ISO_SDU_Length shall be set to zero.
891 	 *
892 	 * (2) Any error status received from the LL via the PDU status should
893 	 *     set the relevant error conditions
894 	 *
895 	 * (3) Forcing lost data when receiving only padding PDUs for any SDU
896 	 *
897 	 *     https://bluetooth.atlassian.net/browse/ES-22876
898 	 *     Request for Clarification - Recombination actions when only
899 	 *     padding unframed PDUs are received:
900 	 *     The clarification was to be rejected, but the discussion in the
901 	 *     comments from March 3rd 2023 were interpretted as "We are
902 	 *     expecting a PDU which ISOAL should convert into an SDU;
903 	 *     instead we receive a padding PDU, which we cannot turn into a
904 	 *     SDU, so the SDU wasn't received at all, and should be reported
905 	 *     as such".
906 	 *
907 	 * (4) Missing end fragment handling.
908 	 */
909 	if (seq_err) {
910 		sp->sdu_status |= ISOAL_SDU_STATUS_LOST_DATA;
911 	} else if (pdu_err && !pdu_padding) {
912 		sp->sdu_status |= meta->status;
913 	} else if (last_pdu && sp->only_padding) {
914 		/* Force lost data if only padding PDUs */
915 		sp->sdu_status |= ISOAL_SDU_STATUS_LOST_DATA;
916 	} else if (last_pdu && (llid != PDU_BIS_LLID_COMPLETE_END) &&
917 				(sp->fsm  != ISOAL_ERR_SPOOL)) {
918 		/* END fragment never seen */
919 		sp->sdu_status |= ISOAL_SDU_STATUS_ERRORS;
920 	}
921 
922 	/* Append valid PDU to SDU */
923 	if (sp->fsm != ISOAL_ERR_SPOOL && (!pdu_padding || end_of_packet)) {
924 		/* If only padding PDUs are received, an SDU should be released
925 		 * as missing (lost data) even if there are no actual errors.
926 		 * (Refer to error prioritisation above for details).
927 		 */
928 		bool append_as_padding = pdu_padding && !sp->only_padding;
929 		err |= isoal_rx_append_to_sdu(sink, pdu_meta, 0,
930 					      length, end_of_packet,
931 					      append_as_padding);
932 	}
933 
934 	/* Update next state */
935 	sp->fsm = next_state;
936 	sp->prev_pdu_id = meta->payload_number;
937 	sp->prev_pdu_is_end = !pdu_err && llid == PDU_BIS_LLID_COMPLETE_END;
938 	sp->prev_pdu_is_padding = !pdu_err && pdu_padding;
939 
940 	sp->initialized = true;
941 
942 	return err;
943 }
944 
945 /* Check a given segment for errors */
isoal_check_seg_header(struct pdu_iso_sdu_sh * seg_hdr,uint8_t pdu_size_remaining)946 static isoal_sdu_status_t isoal_check_seg_header(struct pdu_iso_sdu_sh *seg_hdr,
947 						   uint8_t pdu_size_remaining)
948 {
949 	if (!seg_hdr) {
950 		/* Segment header is null */
951 		return ISOAL_SDU_STATUS_ERRORS;
952 	}
953 
954 	if (pdu_size_remaining >= PDU_ISO_SEG_HDR_SIZE &&
955 		pdu_size_remaining >= PDU_ISO_SEG_HDR_SIZE + seg_hdr->len) {
956 
957 		/* Valid if there is sufficient data for the segment header and
958 		 * there is sufficient data for the required length of the
959 		 * segment
960 		 */
961 		return ISOAL_SDU_STATUS_VALID;
962 	}
963 
964 	/* Data is missing from the PDU */
965 	return ISOAL_SDU_STATUS_LOST_DATA;
966 }
967 
968 /* Check available time reference and release any missing / lost SDUs
969  *
970  * Time tracking and release of lost SDUs for framed:
971  *
972  * Time tracking is implemented based on using the incoming time-stamps of the
973  * PDUs, which should correspond to the BIG / CIG reference anchorpoint of the
974  * current event, to track how time has advanced. The reference used is the
975  * reconstructed SDU synchronisation reference point. For the CIS peripheral and
976  * BIS receiver, this reference is ahead of the time-stamp (anchorpoint),
977  * however for the CIS central this reference will be before (i.e. in the past).
978  * Where the time offset is not available, an ISO interval is used in place of
979  * the time offset to create an approximate reference.
980  *
981  * This information is in-turn used to decided if SDUs are missing or lost and
982  * when they should be released. This approach is inherrently bursty with the
983  * most probable worst case burst being 2 x (ISO interval / SDU Interval) SDUs,
984  * which would occur when only padding is seen in one event followed by all the
985  * SDUs from the next event in one PDU.
986  */
isoal_rx_framed_release_lost_sdus(struct isoal_sink * sink,const struct isoal_pdu_rx * pdu_meta,bool timestamp_valid,uint32_t next_sdu_timestamp)987 static isoal_status_t isoal_rx_framed_release_lost_sdus(struct isoal_sink *sink,
988 							const struct isoal_pdu_rx *pdu_meta,
989 							bool timestamp_valid,
990 							uint32_t next_sdu_timestamp)
991 {
992 	struct isoal_sink_session *session;
993 	struct isoal_sdu_production *sp;
994 	struct isoal_sdu_produced *sdu;
995 	isoal_status_t err;
996 	uint32_t time_elapsed;
997 
998 	sp = &sink->sdu_production;
999 	session = &sink->session;
1000 	sdu = &sp->sdu;
1001 
1002 	err = ISOAL_STATUS_OK;
1003 
1004 	if (isoal_get_time_diff(sdu->timestamp, next_sdu_timestamp, &time_elapsed)) {
1005 		/* Time elapsed >= 0 */
1006 		uint8_t lost_sdus;
1007 
1008 		if (timestamp_valid) {
1009 			/* If there is a valid new time reference, then
1010 			 * calculate the gap between the next SDUs expected
1011 			 * time stamp and the actual reference, rounding at the
1012 			 * mid point.
1013 			 * 0  Next SDU is the SDU that provided the new time
1014 			 *    reference, no lost SDUs
1015 			 * >0 Number of lost SDUs
1016 			 */
1017 			lost_sdus = (time_elapsed + (session->sdu_interval / 2)) /
1018 						session->sdu_interval;
1019 			ISOAL_LOG_DBGV("[%p] Next SDU timestamp (%lu) accurate",
1020 				       sink, next_sdu_timestamp);
1021 		} else {
1022 			/* If there is no valid new time reference, then lost
1023 			 * SDUs should only be released for every full
1024 			 * SDU interval. This should include consideration that
1025 			 * the next expected SDU's time stamp is the base for
1026 			 * time_elapsed (i.e. +1).
1027 			 */
1028 			ISOAL_LOG_DBGV("[%p] Next SDU timestamp (%lu) approximate",
1029 				       sink, next_sdu_timestamp);
1030 			lost_sdus = time_elapsed ? (time_elapsed / session->sdu_interval) + 1 : 0;
1031 		}
1032 
1033 		ISOAL_LOG_DBGV("[%p] Releasing %u lost SDUs", sink, lost_sdus);
1034 
1035 		while (lost_sdus > 0 && !err) {
1036 			sp->sdu_status |= ISOAL_SDU_STATUS_LOST_DATA;
1037 
1038 			err = isoal_rx_append_to_sdu(sink, pdu_meta, 0, 0, true, false);
1039 			lost_sdus--;
1040 		}
1041 	}
1042 
1043 	return err;
1044 }
1045 
1046 /* Update time tracking after release of an SDU.
1047  * At present only required for framed PDUs.
1048  */
isoal_rx_framed_update_sdu_release(struct isoal_sink * sink)1049 static void isoal_rx_framed_update_sdu_release(struct isoal_sink *sink)
1050 {
1051 	struct isoal_sink_session *session;
1052 	struct isoal_sdu_production *sp;
1053 	struct isoal_sdu_produced *sdu;
1054 	uint32_t timestamp;
1055 
1056 	sp = &sink->sdu_production;
1057 	session = &sink->session;
1058 	sdu = &sp->sdu;
1059 
1060 	if (session->framed) {
1061 		/* Update to the expected release time of the next SDU */
1062 		timestamp = isoal_get_wrapped_time_us(sdu->timestamp, session->sdu_interval);
1063 		SET_RX_SDU_TIMESTAMP(sink, sdu->timestamp, timestamp);
1064 	}
1065 }
1066 
1067 /**
1068  * @brief Consume a framed PDU: Copy contents into SDU(s) and emit to a sink
1069  * @details Destination sink may have an already partially built SDU
1070  *
1071  * @param sink[in,out]   Destination sink with bookkeeping state
1072  * @param pdu_meta[out]  PDU with meta information (origin, timing, status)
1073  *
1074  * @return Status
1075  */
isoal_rx_framed_consume(struct isoal_sink * sink,const struct isoal_pdu_rx * pdu_meta)1076 static isoal_status_t isoal_rx_framed_consume(struct isoal_sink *sink,
1077 					      const struct isoal_pdu_rx *pdu_meta)
1078 {
1079 	struct isoal_sink_session *session;
1080 	struct isoal_sdu_production *sp;
1081 	struct isoal_sdu_produced *sdu;
1082 	struct pdu_iso_sdu_sh *seg_hdr;
1083 	struct node_rx_iso_meta *meta;
1084 	uint32_t iso_interval_us;
1085 	uint32_t anchorpoint;
1086 	uint8_t *end_of_pdu;
1087 	uint32_t timeoffset;
1088 	isoal_status_t err;
1089 	uint8_t next_state;
1090 	uint32_t timestamp;
1091 	bool pdu_padding;
1092 	int32_t latency;
1093 	bool pdu_err;
1094 	bool seq_err;
1095 	bool seg_err;
1096 
1097 	sp = &sink->sdu_production;
1098 	session = &sink->session;
1099 	meta = pdu_meta->meta;
1100 	sdu = &sp->sdu;
1101 
1102 	iso_interval_us = session->iso_interval * ISO_INT_UNIT_US;
1103 
1104 	err = ISOAL_STATUS_OK;
1105 	next_state = ISOAL_START;
1106 	pdu_err = (pdu_meta->meta->status != ISOAL_PDU_STATUS_VALID);
1107 	pdu_padding = (pdu_meta->pdu->len == 0);
1108 
1109 	if (sp->fsm == ISOAL_START) {
1110 		seq_err = false;
1111 	} else {
1112 		seq_err = (meta->payload_number != (sp->prev_pdu_id + 1));
1113 	}
1114 
1115 	end_of_pdu = ((uint8_t *) pdu_meta->pdu->payload) + pdu_meta->pdu->len - 1UL;
1116 	seg_hdr = (pdu_err || seq_err || pdu_padding) ? NULL :
1117 			(struct pdu_iso_sdu_sh *) pdu_meta->pdu->payload;
1118 
1119 	seg_err = false;
1120 	if (seg_hdr && isoal_check_seg_header(seg_hdr, pdu_meta->pdu->len) ==
1121 								ISOAL_SDU_STATUS_LOST_DATA) {
1122 		seg_err = true;
1123 		seg_hdr = NULL;
1124 	}
1125 
1126 	/* Calculate an approximate timestamp */
1127 	timestamp = isoal_get_wrapped_time_us(meta->timestamp,
1128 						session->sdu_sync_const - iso_interval_us);
1129 	if (!sp->initialized) {
1130 		/* This should be the first PDU received in this session */
1131 		/* Initialize a temporary timestamp for the next SDU */
1132 		SET_RX_SDU_TIMESTAMP(sink, sdu->timestamp, timestamp);
1133 	}
1134 
1135 	if (pdu_padding && !pdu_err && !seq_err) {
1136 		/* Check and release missed SDUs on receiving padding PDUs */
1137 		ISOAL_LOG_DBGV("[%p] Recevied padding", sink);
1138 		err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, false, timestamp);
1139 	}
1140 
1141 	while (seg_hdr) {
1142 		bool append = true;
1143 		const uint8_t sc    = seg_hdr->sc;
1144 		const uint8_t cmplt = seg_hdr->cmplt;
1145 
1146 		if (sp->fsm == ISOAL_START) {
1147 			sp->sdu_status = ISOAL_SDU_STATUS_VALID;
1148 			sp->sdu_state  = BT_ISO_START;
1149 		}
1150 
1151 		ISOAL_LOG_DBGV("[%p] State %s", sink, FSM_TO_STR(sp->fsm));
1152 		switch (sp->fsm) {
1153 		case ISOAL_START:
1154 			if (!sc) {
1155 				/* Start segment, included time-offset */
1156 				timeoffset = seg_hdr->timeoffset;
1157 				anchorpoint = meta->timestamp;
1158 				latency = session->sdu_sync_const;
1159 				timestamp = isoal_get_wrapped_time_us(anchorpoint,
1160 								      latency - timeoffset);
1161 				ISOAL_LOG_DBGV("[%p] Segment Start @TS=%ld", sink, timestamp);
1162 
1163 				err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, true,
1164 									 timestamp);
1165 				SET_RX_SDU_TIMESTAMP(sink, sdu->timestamp, timestamp);
1166 
1167 				if (cmplt) {
1168 					/* The start of a new SDU that contains the full SDU data in
1169 					 * the current PDU.
1170 					 */
1171 					ISOAL_LOG_DBGV("[%p] Segment Single", sink);
1172 					next_state = ISOAL_START;
1173 				} else {
1174 					/* The start of a new SDU, where not all SDU data is
1175 					 * included in the current PDU, and additional PDUs are
1176 					 * required to complete the SDU.
1177 					 */
1178 					next_state = ISOAL_CONTINUE;
1179 				}
1180 
1181 			} else {
1182 				/* Unsupported case */
1183 				err = ISOAL_STATUS_ERR_UNSPECIFIED;
1184 			}
1185 			break;
1186 
1187 		case ISOAL_CONTINUE:
1188 			if (sc && !cmplt) {
1189 				/* The continuation of a previous SDU. The SDU payload is appended
1190 				 * to the previous data and additional PDUs are required to
1191 				 * complete the SDU.
1192 				 */
1193 				ISOAL_LOG_DBGV("[%p] Segment Continue", sink);
1194 				next_state = ISOAL_CONTINUE;
1195 			} else if (sc && cmplt) {
1196 				/* The continuation of a previous SDU.
1197 				 * Frame data is appended to previously received SDU data and
1198 				 * completes in the current PDU.
1199 				 */
1200 				ISOAL_LOG_DBGV("[%p] Segment End", sink);
1201 				next_state = ISOAL_START;
1202 			} else {
1203 				/* Unsupported case */
1204 				err = ISOAL_STATUS_ERR_UNSPECIFIED;
1205 			}
1206 			break;
1207 
1208 		case ISOAL_ERR_SPOOL:
1209 			/* In error state, search for valid next start of SDU */
1210 
1211 			if (!sc) {
1212 				/* Start segment, included time-offset */
1213 				timeoffset = seg_hdr->timeoffset;
1214 				anchorpoint = meta->timestamp;
1215 				latency = session->sdu_sync_const;
1216 				timestamp = isoal_get_wrapped_time_us(anchorpoint,
1217 								      latency - timeoffset);
1218 				ISOAL_LOG_DBGV("[%p] Segment Start @TS=%ld", sink, timestamp);
1219 
1220 				err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, true,
1221 									 timestamp);
1222 				SET_RX_SDU_TIMESTAMP(sink, sdu->timestamp, timestamp);
1223 
1224 				if (cmplt) {
1225 					/* The start of a new SDU that contains the full SDU data
1226 					 * in the current PDU.
1227 					 */
1228 					ISOAL_LOG_DBGV("[%p] Segment Single", sink);
1229 					next_state = ISOAL_START;
1230 				} else {
1231 					/* The start of a new SDU, where not all SDU data is
1232 					 * included in the current PDU, and additional PDUs are
1233 					 * required to complete the SDU.
1234 					 */
1235 					next_state = ISOAL_CONTINUE;
1236 				}
1237 
1238 			} else {
1239 				/* Start not found yet, stay in Error state */
1240 				err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, false,
1241 									 timestamp);
1242 				append = false;
1243 				next_state = ISOAL_ERR_SPOOL;
1244 			}
1245 
1246 			if (next_state != ISOAL_ERR_SPOOL) {
1247 				/* While in the Error state, received a valid start of the next SDU,
1248 				 * so SDU status and sequence number should be updated.
1249 				 */
1250 				sp->sdu_status = ISOAL_SDU_STATUS_VALID;
1251 				/* sp->sdu_state will be set by next_state decided above */
1252 			}
1253 			break;
1254 		}
1255 
1256 		if (append) {
1257 			/* Calculate offset of first payload byte from SDU based on assumption
1258 			 * of No time_offset in header
1259 			 */
1260 			uint8_t offset = ((uint8_t *) seg_hdr) + PDU_ISO_SEG_HDR_SIZE -
1261 					 pdu_meta->pdu->payload;
1262 			uint8_t length = seg_hdr->len;
1263 
1264 			if (!sc) {
1265 				/* time_offset included in header, don't copy offset field to SDU */
1266 				offset = offset + PDU_ISO_SEG_TIMEOFFSET_SIZE;
1267 				length = length - PDU_ISO_SEG_TIMEOFFSET_SIZE;
1268 			}
1269 
1270 			/* Todo: check if effective len=0 what happens then?
1271 			 * We should possibly be able to send empty packets with only time stamp
1272 			 */
1273 			ISOAL_LOG_DBGV("[%p] Appending %lu bytes", sink, length);
1274 			err |= isoal_rx_append_to_sdu(sink, pdu_meta, offset, length, cmplt, false);
1275 		}
1276 
1277 		/* Update next state */
1278 		ISOAL_LOG_DBGV("[%p] Decoding: Next State %s", sink, FSM_TO_STR(next_state));
1279 		sp->fsm = next_state;
1280 
1281 		/* Find next segment header, set to null if past end of PDU */
1282 		seg_hdr = (struct pdu_iso_sdu_sh *) (((uint8_t *) seg_hdr) +
1283 						     seg_hdr->len + PDU_ISO_SEG_HDR_SIZE);
1284 
1285 		if (((uint8_t *) seg_hdr) > end_of_pdu) {
1286 			seg_hdr = NULL;
1287 		} else if (isoal_check_seg_header(seg_hdr,
1288 				(uint8_t)(end_of_pdu + 1UL - ((uint8_t *) seg_hdr))) ==
1289 								ISOAL_SDU_STATUS_LOST_DATA) {
1290 			seg_err = true;
1291 			seg_hdr = NULL;
1292 		}
1293 	}
1294 
1295 	if (pdu_err || seq_err || seg_err) {
1296 		/* When one or more ISO Data PDUs are not received, the receiving device may
1297 		 * discard all SDUs affected by the missing PDUs. Any partially received SDU
1298 		 * may also be discarded.
1299 		 */
1300 		next_state = ISOAL_ERR_SPOOL;
1301 
1302 
1303 		/* This maps directly to the HCI ISO Data packet Packet_Status_Flag by way of the
1304 		 * sdu_status in the SDU emitted.
1305 		 * BT Core V5.3 : Vol 4 HCI I/F : Part G HCI Func. Spec.:
1306 		 * 5.4.5 HCI ISO Data packets : Table 5.2 :
1307 		 * Packet_Status_Flag (in packets sent by the Controller)
1308 		 *   0b00  Valid data. The complete SDU was received correctly.
1309 		 *   0b01  Possibly invalid data. The contents of the ISO_SDU_Fragment may contain
1310 		 *         errors or part of the SDU may be missing. This is reported as "data with
1311 		 *         possible errors".
1312 		 *   0b10  Part(s) of the SDU were not received correctly. This is reported as
1313 		 *         "lost data".
1314 		 */
1315 		isoal_sdu_status_t next_sdu_status = ISOAL_SDU_STATUS_VALID;
1316 		if (seq_err || seg_err) {
1317 			next_sdu_status |= ISOAL_SDU_STATUS_LOST_DATA;
1318 		} else if (pdu_err) {
1319 			next_sdu_status |= meta->status;
1320 		}
1321 
1322 		switch (sp->fsm) {
1323 		case ISOAL_START:
1324 			/* First release lost SDUs and then release a new SDU
1325 			 * with errors. Since we have an SDU to release
1326 			 * following any lost SDUs, lost SDUs handling should be
1327 			 * similar to when a valid timestamp exists.
1328 			 */
1329 			err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, true, timestamp);
1330 			sp->sdu_status = next_sdu_status;
1331 			err |= isoal_rx_append_to_sdu(sink, pdu_meta, 0, 0, true, false);
1332 			break;
1333 
1334 		case ISOAL_CONTINUE:
1335 			/* If error occurs while an SDU is in production,
1336 			 * release the SDU with errors and then check for lost
1337 			 * SDUs. Since the SDU is already in production, the
1338 			 * time stamp already set should be valid.
1339 			 */
1340 			sp->sdu_status = next_sdu_status;
1341 			err |= isoal_rx_append_to_sdu(sink, pdu_meta, 0, 0, true, false);
1342 			err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, false, timestamp);
1343 			break;
1344 
1345 		case ISOAL_ERR_SPOOL:
1346 			err |= isoal_rx_framed_release_lost_sdus(sink, pdu_meta, false, timestamp);
1347 			break;
1348 		}
1349 
1350 		/* Update next state */
1351 		ISOAL_LOG_DBGV("[%p] Error: Next State %s", sink, FSM_TO_STR(next_state));
1352 		sink->sdu_production.fsm = next_state;
1353 	}
1354 
1355 	sp->prev_pdu_id = meta->payload_number;
1356 	sp->initialized = true;
1357 
1358 	return err;
1359 }
1360 
1361 /**
1362  * @brief Deep copy a PDU, recombine into SDU(s)
1363  * @details Recombination will occur individually for every enabled sink
1364  *
1365  * @param sink_hdl[in] Handle of destination sink
1366  * @param pdu_meta[in] PDU along with meta information (origin, timing, status)
1367  * @return Status
1368  */
isoal_rx_pdu_recombine(isoal_sink_handle_t sink_hdl,const struct isoal_pdu_rx * pdu_meta)1369 isoal_status_t isoal_rx_pdu_recombine(isoal_sink_handle_t sink_hdl,
1370 				      const struct isoal_pdu_rx *pdu_meta)
1371 {
1372 	struct isoal_sink *sink = &isoal_global.sink_state[sink_hdl];
1373 	isoal_status_t err = ISOAL_STATUS_OK;
1374 
1375 	if (sink && sink->sdu_production.mode != ISOAL_PRODUCTION_MODE_DISABLED) {
1376 		if (sink->session.framed) {
1377 			err = isoal_rx_framed_consume(sink, pdu_meta);
1378 		} else {
1379 			err = isoal_rx_unframed_consume(sink, pdu_meta);
1380 		}
1381 	}
1382 
1383 	return err;
1384 }
1385 #endif /* CONFIG_BT_CTLR_SYNC_ISO || CONFIG_BT_CTLR_CONN_ISO */
1386 
1387 #if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
1388 /**
1389  * @brief Find free source from statically-sized pool and allocate it
1390  * @details Implemented as linear search since pool is very small
1391  *
1392  * @param hdl[out]  Handle to source
1393  * @return ISOAL_STATUS_OK if we could allocate; otherwise ISOAL_STATUS_ERR_SOURCE_ALLOC
1394  */
isoal_source_allocate(isoal_source_handle_t * hdl)1395 static isoal_status_t isoal_source_allocate(isoal_source_handle_t *hdl)
1396 {
1397 	isoal_source_handle_t i;
1398 
1399 	/* Very small linear search to find first free */
1400 	for (i = 0; i < CONFIG_BT_CTLR_ISOAL_SOURCES; i++) {
1401 		if (isoal_global.source_allocated[i] == ISOAL_ALLOC_STATE_FREE) {
1402 			isoal_global.source_allocated[i] = ISOAL_ALLOC_STATE_TAKEN;
1403 			*hdl = i;
1404 			return ISOAL_STATUS_OK;
1405 		}
1406 	}
1407 
1408 	return ISOAL_STATUS_ERR_SOURCE_ALLOC; /* All entries were taken */
1409 }
1410 
1411 /**
1412  * @brief Mark a source as being free to allocate again
1413  * @param hdl[in]  Handle to source
1414  */
isoal_source_deallocate(isoal_source_handle_t hdl)1415 static void isoal_source_deallocate(isoal_source_handle_t hdl)
1416 {
1417 	struct isoal_pdu_production *pp;
1418 	struct isoal_source *source;
1419 
1420 	source = &isoal_global.source_state[hdl];
1421 	pp = &source->pdu_production;
1422 
1423 	if (pp->pdu_available > 0) {
1424 		/* There is a pending PDU that should be released */
1425 		if (source && source->session.pdu_release) {
1426 			source->session.pdu_release(pp->pdu.contents.handle,
1427 						    source->session.handle,
1428 						    ISOAL_STATUS_ERR_PDU_EMIT);
1429 		}
1430 	}
1431 
1432 	isoal_global.source_allocated[hdl] = ISOAL_ALLOC_STATE_FREE;
1433 	(void)memset(&isoal_global.source_state[hdl], 0, sizeof(struct isoal_source));
1434 }
1435 
1436 /**
1437  * @brief      Check if a provided handle is valid
1438  * @param[in]  hdl Input handle for validation
1439  * @return         Handle valid / not valid
1440  */
isoal_check_source_hdl_valid(isoal_source_handle_t hdl)1441 static isoal_status_t isoal_check_source_hdl_valid(isoal_source_handle_t hdl)
1442 {
1443 	if (hdl < CONFIG_BT_CTLR_ISOAL_SOURCES &&
1444 		isoal_global.source_allocated[hdl] == ISOAL_ALLOC_STATE_TAKEN) {
1445 		return ISOAL_STATUS_OK;
1446 	}
1447 
1448 	LOG_ERR("Invalid source handle (0x%02x)", hdl);
1449 
1450 	return ISOAL_STATUS_ERR_UNSPECIFIED;
1451 }
1452 
1453 /**
1454  * @brief Create a new source
1455  *
1456  * @param handle[in]            Connection handle
1457  * @param role[in]              Peripheral, Central or Broadcast
1458  * @param framed[in]            Framed case
1459  * @param burst_number[in]      Burst Number
1460  * @param flush_timeout[in]     Flush timeout
1461  * @param max_octets[in]        Maximum PDU size (Max_PDU_C_To_P / Max_PDU_P_To_C)
1462  * @param sdu_interval[in]      SDU interval
1463  * @param iso_interval[in]      ISO interval
1464  * @param stream_sync_delay[in] CIS / BIS sync delay
1465  * @param group_sync_delay[in]  CIG / BIG sync delay
1466  * @param pdu_alloc[in]         Callback of PDU allocator
1467  * @param pdu_write[in]         Callback of PDU byte writer
1468  * @param pdu_emit[in]          Callback of PDU emitter
1469  * @param pdu_release[in]       Callback of PDU deallocator
1470  * @param hdl[out]              Handle to new source
1471  *
1472  * @return ISOAL_STATUS_OK if we could create a new sink; otherwise ISOAL_STATUS_ERR_SOURCE_ALLOC
1473  */
isoal_source_create(uint16_t handle,uint8_t role,uint8_t framed,uint8_t burst_number,uint8_t flush_timeout,uint8_t max_octets,uint32_t sdu_interval,uint16_t iso_interval,uint32_t stream_sync_delay,uint32_t group_sync_delay,isoal_source_pdu_alloc_cb pdu_alloc,isoal_source_pdu_write_cb pdu_write,isoal_source_pdu_emit_cb pdu_emit,isoal_source_pdu_release_cb pdu_release,isoal_source_handle_t * hdl)1474 isoal_status_t isoal_source_create(
1475 	uint16_t                    handle,
1476 	uint8_t                     role,
1477 	uint8_t                     framed,
1478 	uint8_t                     burst_number,
1479 	uint8_t                     flush_timeout,
1480 	uint8_t                     max_octets,
1481 	uint32_t                    sdu_interval,
1482 	uint16_t                    iso_interval,
1483 	uint32_t                    stream_sync_delay,
1484 	uint32_t                    group_sync_delay,
1485 	isoal_source_pdu_alloc_cb   pdu_alloc,
1486 	isoal_source_pdu_write_cb   pdu_write,
1487 	isoal_source_pdu_emit_cb    pdu_emit,
1488 	isoal_source_pdu_release_cb pdu_release,
1489 	isoal_source_handle_t       *hdl)
1490 {
1491 	isoal_status_t err;
1492 
1493 	/* Allocate a new source */
1494 	err = isoal_source_allocate(hdl);
1495 	if (err) {
1496 		return err;
1497 	}
1498 
1499 	struct isoal_source_session *session = &isoal_global.source_state[*hdl].session;
1500 
1501 	session->handle = handle;
1502 	session->framed = framed;
1503 	session->burst_number = burst_number;
1504 	session->iso_interval = iso_interval;
1505 	session->sdu_interval = sdu_interval;
1506 
1507 	/* Todo: Next section computing various constants, should potentially be a
1508 	 * function in itself as a number of the dependencies could be changed while
1509 	 * a connection is active.
1510 	 */
1511 
1512 	/* Note: sdu_interval unit is uS, iso_interval is a multiple of 1.25mS */
1513 	session->pdus_per_sdu = (burst_number * sdu_interval) /
1514 		((uint32_t)iso_interval * ISO_INT_UNIT_US);
1515 	/* Set maximum PDU size */
1516 	session->max_pdu_size = max_octets;
1517 
1518 	/* Remember the platform-specific callbacks */
1519 	session->pdu_alloc   = pdu_alloc;
1520 	session->pdu_write   = pdu_write;
1521 	session->pdu_emit    = pdu_emit;
1522 	session->pdu_release = pdu_release;
1523 
1524 	/* TODO: Constant need to be updated */
1525 
1526 	/* Initialize running seq number to zero */
1527 	session->sn = 0;
1528 
1529 	return err;
1530 }
1531 
1532 /**
1533  * @brief Atomically enable latch-in of packets and PDU production
1534  * @param hdl[in]  Handle of existing instance
1535  */
isoal_source_enable(isoal_source_handle_t hdl)1536 void isoal_source_enable(isoal_source_handle_t hdl)
1537 {
1538 	/* Reset bookkeeping state */
1539 	memset(&isoal_global.source_state[hdl].pdu_production, 0,
1540 	       sizeof(isoal_global.source_state[hdl].pdu_production));
1541 
1542 	/* Atomically enable */
1543 	isoal_global.source_state[hdl].pdu_production.mode = ISOAL_PRODUCTION_MODE_ENABLED;
1544 }
1545 
1546 /**
1547  * @brief Atomically disable latch-in of packets and PDU production
1548  * @param hdl[in]  Handle of existing instance
1549  */
isoal_source_disable(isoal_source_handle_t hdl)1550 void isoal_source_disable(isoal_source_handle_t hdl)
1551 {
1552 	/* Atomically disable */
1553 	isoal_global.source_state[hdl].pdu_production.mode = ISOAL_PRODUCTION_MODE_DISABLED;
1554 }
1555 
1556 /**
1557  * @brief Disable and deallocate existing source
1558  * @param hdl[in]  Handle of existing instance
1559  */
isoal_source_destroy(isoal_source_handle_t hdl)1560 void isoal_source_destroy(isoal_source_handle_t hdl)
1561 {
1562 	/* Atomic disable */
1563 	isoal_source_disable(hdl);
1564 
1565 	/* Permit allocation anew */
1566 	isoal_source_deallocate(hdl);
1567 }
1568 
1569 /**
1570  * Queue the PDU in production in the relevant LL transmit queue. If the
1571  * attmept to release the PDU fails, the buffer linked to the PDU will be released
1572  * and it will not be possible to retry the emit operation on the same PDU.
1573  * @param[in]  source_ctx        ISO-AL source reference for this CIS / BIS
1574  * @param[in]  produced_pdu      PDU in production
1575  * @param[in]  pdu_ll_id         LLID to be set indicating the type of fragment
1576  * @param[in]  sdu_fragments     Nummber of SDU HCI fragments consumed
1577  * @param[in]  payload_number    CIS / BIS payload number
1578  * @param[in]  payload_size      Length of the data written to the PDU
1579  * @return     Error status of the operation
1580  */
isoal_tx_pdu_emit(const struct isoal_source * source_ctx,const struct isoal_pdu_produced * produced_pdu,const uint8_t pdu_ll_id,const uint8_t sdu_fragments,const uint64_t payload_number,const isoal_pdu_len_t payload_size)1581 static isoal_status_t isoal_tx_pdu_emit(const struct isoal_source *source_ctx,
1582 					const struct isoal_pdu_produced *produced_pdu,
1583 					const uint8_t pdu_ll_id,
1584 					const uint8_t sdu_fragments,
1585 					const uint64_t payload_number,
1586 					const isoal_pdu_len_t payload_size)
1587 {
1588 	struct node_tx_iso *node_tx;
1589 	isoal_status_t status;
1590 	uint16_t handle;
1591 
1592 	/* Retrieve CIS / BIS handle */
1593 	handle = bt_iso_handle(source_ctx->session.handle);
1594 
1595 	/* Retrieve Node handle */
1596 	node_tx = produced_pdu->contents.handle;
1597 	/* Under race condition with isoal_source_deallocate() */
1598 	if (!node_tx) {
1599 		return ISOAL_STATUS_ERR_PDU_EMIT;
1600 	}
1601 
1602 	/* Set payload number */
1603 	node_tx->payload_count = payload_number & 0x7fffffffff;
1604 	node_tx->sdu_fragments = sdu_fragments;
1605 	/* Set PDU LLID */
1606 	produced_pdu->contents.pdu->ll_id = pdu_ll_id;
1607 	/* Set PDU length */
1608 	produced_pdu->contents.pdu->len = (uint8_t)payload_size;
1609 
1610 	/* Attempt to enqueue the node towards the LL */
1611 	status = source_ctx->session.pdu_emit(node_tx, handle);
1612 
1613 	if (status != ISOAL_STATUS_OK) {
1614 		/* If it fails, the node will be released and no further attempt
1615 		 * will be possible
1616 		 */
1617 		LOG_ERR("Failed to enqueue node (%p)", node_tx);
1618 		source_ctx->session.pdu_release(node_tx, handle, status);
1619 	}
1620 
1621 	return status;
1622 }
1623 
1624 /**
1625  * Allocates a new PDU only if the previous PDU was emitted
1626  * @param[in]  source      ISO-AL source reference
1627  * @param[in]  tx_sdu      SDU fragment to be transmitted (can be NULL)
1628  * @return     Error status of operation
1629  */
isoal_tx_allocate_pdu(struct isoal_source * source,const struct isoal_sdu_tx * tx_sdu)1630 static isoal_status_t isoal_tx_allocate_pdu(struct isoal_source *source,
1631 					    const struct isoal_sdu_tx *tx_sdu)
1632 {
1633 	ARG_UNUSED(tx_sdu);
1634 
1635 	struct isoal_source_session *session;
1636 	struct isoal_pdu_production *pp;
1637 	struct isoal_pdu_produced *pdu;
1638 	isoal_status_t err;
1639 
1640 	err = ISOAL_STATUS_OK;
1641 	session = &source->session;
1642 	pp = &source->pdu_production;
1643 	pdu = &pp->pdu;
1644 
1645 	/* Allocate a PDU if the previous was filled (thus sent) */
1646 	const bool pdu_complete = (pp->pdu_available == 0);
1647 
1648 	if (pdu_complete) {
1649 		/* Allocate new PDU buffer */
1650 		err = session->pdu_alloc(
1651 			&pdu->contents  /* [out] Updated with pointer and size */
1652 		);
1653 
1654 		if (err) {
1655 			pdu->contents.handle = NULL;
1656 			pdu->contents.pdu    = NULL;
1657 			pdu->contents.size   = 0;
1658 		}
1659 
1660 		/* Get maximum buffer available */
1661 		const size_t available_len = MIN(
1662 			session->max_pdu_size,
1663 			pdu->contents.size
1664 		);
1665 
1666 		/* Nothing has been written into buffer yet */
1667 		pp->pdu_written   = 0;
1668 		pp->pdu_available = available_len;
1669 		LL_ASSERT(available_len > 0);
1670 
1671 		pp->pdu_cnt++;
1672 	}
1673 
1674 	return err;
1675 }
1676 
1677 /**
1678  * Attempt to emit the PDU in production if it is complete.
1679  * @param[in]  source      ISO-AL source reference
1680  * @param[in]  force_emit  Request PDU emit
1681  * @param[in]  pdu_ll_id   LLID / PDU fragment type as Start, Cont, End, Single (Unframed) or Framed
1682  * @return     Error status of operation
1683  */
isoal_tx_try_emit_pdu(struct isoal_source * source,bool force_emit,uint8_t pdu_ll_id)1684 static isoal_status_t isoal_tx_try_emit_pdu(struct isoal_source *source,
1685 					    bool force_emit,
1686 					    uint8_t pdu_ll_id)
1687 {
1688 	struct isoal_pdu_production *pp;
1689 	struct isoal_pdu_produced   *pdu;
1690 	isoal_status_t err;
1691 
1692 	err = ISOAL_STATUS_OK;
1693 	pp = &source->pdu_production;
1694 	pdu = &pp->pdu;
1695 
1696 	/* Emit a PDU */
1697 	const bool pdu_complete = (pp->pdu_available == 0) || force_emit;
1698 
1699 	if (force_emit) {
1700 		pp->pdu_available = 0;
1701 	}
1702 
1703 	if (pdu_complete) {
1704 		/* Emit PDU and increment the payload number */
1705 		err = isoal_tx_pdu_emit(source, pdu, pdu_ll_id,
1706 					pp->sdu_fragments,
1707 					pp->payload_number,
1708 					pp->pdu_written);
1709 		pp->payload_number++;
1710 		pp->sdu_fragments = 0;
1711 	}
1712 
1713 	return err;
1714 }
1715 
1716 /**
1717  * @brief Get the next unframed payload number for transmission based on the
1718  *        input meta data in the TX SDU and the current production information.
1719  * @param  source_hdl[in]      Destination source handle
1720  * @param  tx_sdu[in]          SDU with meta data information
1721  * @param  payload_number[out] Next payload number
1722  * @return                     Number of skipped SDUs
1723  */
isoal_tx_unframed_get_next_payload_number(isoal_source_handle_t source_hdl,const struct isoal_sdu_tx * tx_sdu,uint64_t * payload_number)1724 uint16_t isoal_tx_unframed_get_next_payload_number(isoal_source_handle_t source_hdl,
1725 						   const struct isoal_sdu_tx *tx_sdu,
1726 						   uint64_t *payload_number)
1727 {
1728 	struct isoal_source_session *session;
1729 	struct isoal_pdu_production *pp;
1730 	struct isoal_source *source;
1731 	uint16_t sdus_skipped;
1732 
1733 	source      = &isoal_global.source_state[source_hdl];
1734 	session     = &source->session;
1735 	pp          = &source->pdu_production;
1736 
1737 	/* Current payload number should have been updated to match the next
1738 	 * SDU.
1739 	 */
1740 	*payload_number = pp->payload_number;
1741 	sdus_skipped = 0;
1742 
1743 	if (tx_sdu->sdu_state == BT_ISO_START ||
1744 		tx_sdu->sdu_state == BT_ISO_SINGLE) {
1745 		/* Initialize to info provided in SDU */
1746 		bool time_diff_valid;
1747 		uint32_t time_diff;
1748 
1749 		/* Start of a new SDU */
1750 		time_diff_valid = false;
1751 		time_diff = 0;
1752 
1753 		/* Adjust payload number */
1754 		if (IS_ENABLED(CONFIG_BT_CTLR_ISOAL_SN_STRICT) && session->sn) {
1755 			/* Not the first SDU in this session, so reference
1756 			 * information should be valid. At this point, the
1757 			 * current payload number should be at the first PDU of
1758 			 * the incoming SDU, if the SDU is in sequence.
1759 			 * Adjustment is only required for the number of SDUs
1760 			 * skipped beyond the next expected SDU.
1761 			 */
1762 			time_diff_valid = isoal_get_time_diff(session->last_input_time_stamp,
1763 							      tx_sdu->time_stamp,
1764 							      &time_diff);
1765 
1766 			/* Priority is given to the sequence number, however as
1767 			 * there is a possibility that the app may not manage
1768 			 * the sequence number correctly by incrementing every
1769 			 * SDU interval, the time stamp is checked if the
1770 			 * sequence number doesn't change.
1771 			 */
1772 			if (tx_sdu->packet_sn > session->last_input_sn + 1) {
1773 				/* Packet sequence number is not consecutive.
1774 				 * Find the number of skipped SDUs based on the
1775 				 * difference in the packet sequence number.
1776 				 */
1777 				sdus_skipped = (tx_sdu->packet_sn - session->last_input_sn) - 1;
1778 				*payload_number = pp->payload_number +
1779 							(sdus_skipped * session->pdus_per_sdu);
1780 
1781 			} else if (tx_sdu->packet_sn == session->last_input_sn &&
1782 					time_diff_valid && time_diff > session->sdu_interval) {
1783 				/* SDU time stamps are not consecutive if more
1784 				 * than two SDU intervals apart. Find the number
1785 				 * of skipped SDUs based on the difference in
1786 				 * the time stamps.
1787 				 */
1788 				/* Round at mid-point */
1789 				sdus_skipped = ((time_diff + (session->sdu_interval / 2)) /
1790 							session->sdu_interval) - 1;
1791 				*payload_number = pp->payload_number +
1792 							(sdus_skipped * session->pdus_per_sdu);
1793 			} else {
1794 				/* SDU is next in sequence */
1795 			}
1796 		} else {
1797 			/* First SDU, so align with target event */
1798 			/* Update the payload number if the target event is
1799 			 * later than the payload number indicates.
1800 			 */
1801 			*payload_number = MAX(pp->payload_number,
1802 				(tx_sdu->target_event * session->burst_number));
1803 		}
1804 	}
1805 
1806 	return sdus_skipped;
1807 }
1808 
1809 /* NOTE: Use of target_event and grp_ref_point as input from upper layer.
1810  *
1811  * For unframed:
1812  * Before the modification to use the PSN to decide the position of an SDU in a
1813  * stream of SDU, the target event was what was used in deciding the event for
1814  * each SDU. This meant that there would possibly have been skews on the
1815  * receiver for each SDU and we had trouble with LL/CIS/PER/BV-39-C which
1816  * expects clustering within an event.
1817  *
1818  * After the change, the PSN is used to decide the position of an SDU in the
1819  * stream anchored at the first PSN received. However for the first SDU
1820  * (assume that PSN=0), it will be the target event that decides which event
1821  * will be used for the fragmented payloads. Although the same interface from
1822  * the original is retained, the target event and group reference point only
1823  * impacts the event chosen for the first SDU and all subsequent SDUs will be
1824  * decided relative to the first.
1825  *
1826  * The target event and related group reference point is still used to provide
1827  * the ISO-AL with a notion of time, for example when storing information
1828  * required for the TX Sync command. For example if for PSN 4, target event is
1829  * 8 but event 7 is chosen as the correct position for the SDU with PSN 4, the
1830  * group reference point stored is obtained by subtracting an ISO interval from
1831  * the group reference provided with target event 8 to get the BIG/CIG reference
1832  * for event 7. It is also expected that this value is the latest reference and
1833  * is drift compensated.
1834  *
1835  * The PSN alone is not sufficient for this because as far as I am aware, host
1836  * and controller have no common reference time for when CIG/BIG event 0 starts.
1837  * Therefore I would expect it is possible to receive PSN 0 in event 2 for
1838  * example. If the target event provided is event 3, then PSN 0 will be
1839  * fragmented into payloads for event 3 and that will serve as the anchor for
1840  * the stream and subsequent SDUs. If for example target event provided was
1841  * event 2 instead, then it could very well be that PSN 0 might not be
1842  * transmitted as is was received midway through event 2 and the payloads
1843  * expired. If this happens then subsequent SDUs might also all be late for
1844  * their transmission slots as they are positioned relative to PSN 0.
1845  */
1846 
1847 /**
1848  * @brief Fragment received SDU and produce unframed PDUs
1849  * @details Destination source may have an already partially built PDU
1850  *
1851  * @param source_hdl[in] Destination source handle
1852  * @param tx_sdu[in]     SDU with packet boundary information
1853  *
1854  * @return Status
1855  */
isoal_tx_unframed_produce(isoal_source_handle_t source_hdl,const struct isoal_sdu_tx * tx_sdu)1856 static isoal_status_t isoal_tx_unframed_produce(isoal_source_handle_t source_hdl,
1857 						const struct isoal_sdu_tx *tx_sdu)
1858 {
1859 	struct isoal_source_session *session;
1860 	isoal_sdu_len_t packet_available;
1861 	struct isoal_pdu_production *pp;
1862 	struct isoal_source *source;
1863 	const uint8_t *sdu_payload;
1864 	bool zero_length_sdu;
1865 	isoal_status_t err;
1866 	bool padding_pdu;
1867 	uint8_t ll_id;
1868 
1869 	source      = &isoal_global.source_state[source_hdl];
1870 	session     = &source->session;
1871 	pp          = &source->pdu_production;
1872 	padding_pdu = false;
1873 	err         = ISOAL_STATUS_OK;
1874 
1875 	packet_available = tx_sdu->size;
1876 	sdu_payload = tx_sdu->dbuf;
1877 	LL_ASSERT(sdu_payload);
1878 
1879 	zero_length_sdu = (packet_available == 0 &&
1880 		tx_sdu->sdu_state == BT_ISO_SINGLE);
1881 
1882 	if (tx_sdu->sdu_state == BT_ISO_START ||
1883 		tx_sdu->sdu_state == BT_ISO_SINGLE) {
1884 		/* Initialize to info provided in SDU */
1885 		uint32_t actual_grp_ref_point;
1886 		uint64_t next_payload_number;
1887 		uint16_t sdus_skipped;
1888 		uint64_t actual_event;
1889 		bool time_diff_valid;
1890 		uint32_t time_diff;
1891 
1892 		/* Start of a new SDU */
1893 		actual_grp_ref_point = tx_sdu->grp_ref_point;
1894 		sdus_skipped = 0;
1895 		time_diff_valid = false;
1896 		time_diff = 0;
1897 		/* Adjust payload number */
1898 		time_diff_valid = isoal_get_time_diff(session->last_input_time_stamp,
1899 						      tx_sdu->time_stamp,
1900 						      &time_diff);
1901 
1902 		sdus_skipped = isoal_tx_unframed_get_next_payload_number(source_hdl, tx_sdu,
1903 									 &next_payload_number);
1904 		pp->payload_number = next_payload_number;
1905 
1906 		/* Update sequence number for received SDU
1907 		 *
1908 		 * BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
1909 		 * 2 ISOAL Features :
1910 		 * SDUs received by the ISOAL from the upper layer shall be
1911 		 * given a sequence number which is initialized to 0 when the
1912 		 * CIS or BIS is created.
1913 		 *
1914 		 * NOTE: The upper layer may synchronize its sequence number
1915 		 * with the sequence number in the ISOAL once the Datapath is
1916 		 * configured and the link is established.
1917 		 */
1918 		session->sn += sdus_skipped + 1;
1919 
1920 		/* Get actual event for this payload number */
1921 		actual_event = pp->payload_number / session->burst_number;
1922 
1923 		/* Get group reference point for this PDU based on the actual
1924 		 * event being set. This might introduce some errors as the
1925 		 * group refernce point for future events could drift. However
1926 		 * as the time offset calculation requires an absolute value,
1927 		 * this seems to be the best candidate.
1928 		 */
1929 		if (actual_event != tx_sdu->target_event) {
1930 			actual_grp_ref_point =
1931 				isoal_get_wrapped_time_us(tx_sdu->grp_ref_point,
1932 							  (actual_event - tx_sdu->target_event) *
1933 							  session->iso_interval * ISO_INT_UNIT_US);
1934 		}
1935 
1936 		/* Store timing info for TX Sync command */
1937 		session->tx_time_stamp = actual_grp_ref_point;
1938 		/* BT Core V5.3 : Vol 4 HCI : Part E HCI Functional Spec:
1939 		 * 7.8.96 LE Read ISO TX Sync Command:
1940 		 * When the Connection_Handle identifies a CIS or BIS that is
1941 		 * transmitting unframed PDUs the value of Time_Offset returned
1942 		 * shall be zero
1943 		 * Relies on initialization value being 0.
1944 		 */
1945 
1946 		/* Reset PDU fragmentation count for this SDU */
1947 		pp->pdu_cnt = 0;
1948 
1949 		/* The start of an unframed SDU will always be in a new PDU.
1950 		 * There cannot be any other fragments packed.
1951 		 */
1952 		pp->sdu_fragments = 0;
1953 
1954 		/* Update input packet number and time stamp */
1955 		session->last_input_sn = tx_sdu->packet_sn;
1956 
1957 		if (!time_diff_valid || time_diff < session->sdu_interval) {
1958 			/* If the time-stamp is invalid or the difference is
1959 			 * less than an SDU interval, then set the reference
1960 			 * time stamp to what should have been received. This is
1961 			 * done to avoid incorrectly detecting a gap in time
1962 			 * stamp inputs should there be a burst of SDUs
1963 			 * clustered together.
1964 			 */
1965 			session->last_input_time_stamp = isoal_get_wrapped_time_us(
1966 								session->last_input_time_stamp,
1967 								session->sdu_interval);
1968 		} else {
1969 			session->last_input_time_stamp = tx_sdu->time_stamp;
1970 		}
1971 	}
1972 
1973 	/* PDUs should be created until the SDU fragment has been fragmented or
1974 	 * if this is the last fragment of the SDU, until the required padding
1975 	 * PDU(s) are sent.
1976 	 */
1977 	while ((err == ISOAL_STATUS_OK) &&
1978 		((packet_available > 0) || padding_pdu || zero_length_sdu)) {
1979 		const isoal_status_t err_alloc = isoal_tx_allocate_pdu(source, tx_sdu);
1980 		struct isoal_pdu_produced  *pdu = &pp->pdu;
1981 		err |= err_alloc;
1982 
1983 		/*
1984 		 * For this PDU we can only consume of packet, bounded by:
1985 		 *   - What can fit in the destination PDU.
1986 		 *   - What remains of the packet.
1987 		 */
1988 		const size_t consume_len = MIN(
1989 			packet_available,
1990 			pp->pdu_available
1991 		);
1992 
1993 		/* End of the SDU fragment has been reached when the last of the
1994 		 * SDU is packed into a PDU.
1995 		 */
1996 		bool end_of_sdu_frag = !padding_pdu &&
1997 				((consume_len > 0 && consume_len == packet_available) ||
1998 					zero_length_sdu);
1999 
2000 		if (consume_len > 0) {
2001 			err |= session->pdu_write(&pdu->contents,
2002 						  pp->pdu_written,
2003 						  sdu_payload,
2004 						  consume_len);
2005 			sdu_payload       += consume_len;
2006 			pp->pdu_written   += consume_len;
2007 			pp->pdu_available -= consume_len;
2008 			packet_available  -= consume_len;
2009 		}
2010 
2011 		if (end_of_sdu_frag) {
2012 			/* Each PDU will carry the number of completed SDU
2013 			 * fragments contained in that PDU.
2014 			 */
2015 			pp->sdu_fragments++;
2016 		}
2017 
2018 		/* End of the SDU is reached at the end of the last SDU fragment
2019 		 * or if this is a single fragment SDU
2020 		 */
2021 		bool end_of_sdu = (packet_available == 0) &&
2022 				((tx_sdu->sdu_state == BT_ISO_SINGLE) ||
2023 					(tx_sdu->sdu_state == BT_ISO_END));
2024 
2025 		/* Decide PDU type
2026 		 *
2027 		 * BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
2028 		 * 2.1 Unframed PDU :
2029 		 * LLID 0b00 PDU_BIS_LLID_COMPLETE_END:
2030 		 * (1) When the payload of the ISO Data PDU contains the end
2031 		 *     fragment of an SDU.
2032 		 * (2) When the payload of the ISO Data PDU contains a complete
2033 		 *     SDU.
2034 		 * (3) When an SDU contains zero length data, the corresponding
2035 		 *     PDU shall be of zero length and the LLID field shall be
2036 		 *     set to 0b00.
2037 		 *
2038 		 * LLID 0b01 PDU_BIS_LLID_COMPLETE_END:
2039 		 * (1) When the payload of the ISO Data PDU contains a start or
2040 		 *     a continuation fragment of an SDU.
2041 		 * (2) When the ISO Data PDU is used as padding.
2042 		 */
2043 		ll_id = PDU_BIS_LLID_COMPLETE_END;
2044 		if (!end_of_sdu || padding_pdu) {
2045 			ll_id = PDU_BIS_LLID_START_CONTINUE;
2046 		}
2047 
2048 		const isoal_status_t err_emit = isoal_tx_try_emit_pdu(source, end_of_sdu, ll_id);
2049 
2050 		err |= err_emit;
2051 
2052 		/* Send padding PDU(s) if required
2053 		 *
2054 		 * BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
2055 		 * 2.1 Unframed PDU :
2056 		 * Each SDU shall generate BN ÷ (ISO_Interval ÷ SDU_Interval)
2057 		 * fragments. If an SDU generates less than this number of
2058 		 * fragments, empty payloads shall be used to make up the
2059 		 * number.
2060 		 */
2061 		padding_pdu = (end_of_sdu && (pp->pdu_cnt < session->pdus_per_sdu));
2062 		zero_length_sdu = false;
2063 	}
2064 
2065 	return err;
2066 }
2067 
2068 /**
2069  * @brief  Inserts a segmentation header at the current write point in the PDU
2070  *         under production.
2071  * @param  source              source handle
2072  * @param  sc                  start / continuation bit value to be written
2073  * @param  cmplt               complete bit value to be written
2074  * @param  time_offset         value of time offset to be written
2075  * @return                     status
2076  */
isoal_insert_seg_header_timeoffset(struct isoal_source * source,const bool sc,const bool cmplt,const uint32_t time_offset)2077 static isoal_status_t isoal_insert_seg_header_timeoffset(struct isoal_source *source,
2078 							 const bool sc,
2079 							 const bool cmplt,
2080 							 const uint32_t time_offset)
2081 {
2082 	struct isoal_source_session *session;
2083 	struct isoal_pdu_production *pp;
2084 	struct isoal_pdu_produced *pdu;
2085 	struct pdu_iso_sdu_sh seg_hdr;
2086 	isoal_status_t err;
2087 	uint8_t write_size;
2088 
2089 	session    = &source->session;
2090 	pp         = &source->pdu_production;
2091 	pdu        = &pp->pdu;
2092 	write_size = PDU_ISO_SEG_HDR_SIZE + (sc ? 0 : PDU_ISO_SEG_TIMEOFFSET_SIZE);
2093 
2094 	memset(&seg_hdr, 0, sizeof(seg_hdr));
2095 
2096 	/* Check if there is enough space left in the PDU. This should not fail
2097 	 * as the calling should also check before requesting insertion of a
2098 	 * new header.
2099 	 */
2100 	if (pp->pdu_available < write_size) {
2101 
2102 		return ISOAL_STATUS_ERR_UNSPECIFIED;
2103 	}
2104 
2105 	seg_hdr.sc = sc;
2106 	seg_hdr.cmplt = cmplt;
2107 	seg_hdr.len = sc ? 0 : PDU_ISO_SEG_TIMEOFFSET_SIZE;
2108 
2109 	if (!sc) {
2110 		seg_hdr.timeoffset = time_offset;
2111 	}
2112 
2113 	/* Store header */
2114 	pp->seg_hdr_sc = seg_hdr.sc;
2115 	pp->seg_hdr_length = seg_hdr.len;
2116 
2117 	/* Save location of last segmentation header so that it can be updated
2118 	 * as data is written.
2119 	 */
2120 	pp->last_seg_hdr_loc = pp->pdu_written;
2121 	/* Write to PDU */
2122 	err = session->pdu_write(&pdu->contents,
2123 				  pp->pdu_written,
2124 				  (uint8_t *) &seg_hdr,
2125 				  write_size);
2126 	pp->pdu_written   += write_size;
2127 	pp->pdu_available -= write_size;
2128 
2129 	return err;
2130 }
2131 
2132 /**
2133  * @breif  Updates the cmplt flag and length in the last segmentation header written
2134  * @param  source     source handle
2135  * @param  cmplt      ew value for complete flag
2136  * param   add_length length to add
2137  * @return            status
2138  */
isoal_update_seg_header_cmplt_length(struct isoal_source * source,const bool cmplt,const uint8_t add_length)2139 static isoal_status_t isoal_update_seg_header_cmplt_length(struct isoal_source *source,
2140 							   const bool cmplt,
2141 							   const uint8_t add_length)
2142 {
2143 	struct isoal_source_session *session;
2144 	struct isoal_pdu_production *pp;
2145 	struct isoal_pdu_produced *pdu;
2146 	struct pdu_iso_sdu_sh seg_hdr;
2147 
2148 	session    = &source->session;
2149 	pp         = &source->pdu_production;
2150 	pdu        = &pp->pdu;
2151 	memset(&seg_hdr, 0, sizeof(seg_hdr));
2152 
2153 	seg_hdr.sc = pp->seg_hdr_sc;
2154 
2155 	/* Update the complete flag and length */
2156 	seg_hdr.cmplt = cmplt;
2157 	pp->seg_hdr_length += add_length;
2158 	seg_hdr.len = pp->seg_hdr_length;
2159 
2160 
2161 	/* Re-write the segmentation header at the same location */
2162 	return session->pdu_write(&pdu->contents,
2163 				  pp->last_seg_hdr_loc,
2164 				  (uint8_t *) &seg_hdr,
2165 				  PDU_ISO_SEG_HDR_SIZE);
2166 }
2167 
2168 /**
2169  * @brief Fragment received SDU and produce framed PDUs
2170  * @details Destination source may have an already partially built PDU
2171  *
2172  * @param source[in,out] Destination source with bookkeeping state
2173  * @param tx_sdu[in]     SDU with packet boundary information
2174  *
2175  * @return Status
2176  */
isoal_tx_framed_produce(isoal_source_handle_t source_hdl,const struct isoal_sdu_tx * tx_sdu)2177 static isoal_status_t isoal_tx_framed_produce(isoal_source_handle_t source_hdl,
2178 						const struct isoal_sdu_tx *tx_sdu)
2179 {
2180 	struct isoal_source_session *session;
2181 	struct isoal_pdu_production *pp;
2182 	isoal_sdu_len_t packet_available;
2183 	struct isoal_source *source;
2184 	const uint8_t *sdu_payload;
2185 	uint32_t time_offset;
2186 	bool zero_length_sdu;
2187 	isoal_status_t err;
2188 	bool padding_pdu;
2189 	uint8_t ll_id;
2190 
2191 	source      = &isoal_global.source_state[source_hdl];
2192 	session     = &source->session;
2193 	pp          = &source->pdu_production;
2194 	padding_pdu = false;
2195 	err         = ISOAL_STATUS_OK;
2196 	time_offset = 0;
2197 
2198 	packet_available = tx_sdu->size;
2199 	sdu_payload      = tx_sdu->dbuf;
2200 	LL_ASSERT(sdu_payload);
2201 
2202 	zero_length_sdu = (packet_available == 0 &&
2203 		tx_sdu->sdu_state == BT_ISO_SINGLE);
2204 
2205 	if (tx_sdu->sdu_state == BT_ISO_START ||
2206 		tx_sdu->sdu_state == BT_ISO_SINGLE) {
2207 		/* Start of a new SDU */
2208 
2209 		/* Initialize to info provided in SDU */
2210 		uint32_t actual_grp_ref_point = tx_sdu->grp_ref_point;
2211 		uint64_t actual_event = tx_sdu->target_event;
2212 		bool time_diff_valid = false;
2213 		uint32_t time_diff = 0;
2214 
2215 		/* Update sequence number for received SDU
2216 		 *
2217 		 * BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
2218 		 * 2 ISOAL Features :
2219 		 * SDUs received by the ISOAL from the upper layer shall be
2220 		 * given a sequence number which is initialized to 0 when the
2221 		 * CIS or BIS is created.
2222 		 *
2223 		 * NOTE: The upper layer may synchronize its sequence number
2224 		 * with the sequence number in the ISOAL once the Datapath is
2225 		 * configured and the link is established.
2226 		 */
2227 		session->sn++;
2228 
2229 		/* Reset PDU production state */
2230 		pp->pdu_state = BT_ISO_START;
2231 
2232 		/* Update payload counter in case time has passed since the last
2233 		 * SDU. This should mean that event count * burst number should
2234 		 * be greater than the current payload number. In the event of
2235 		 * an SDU interval smaller than the ISO interval, multiple SDUs
2236 		 * will be sent in the same event. As such the current payload
2237 		 * number should be retained. Payload numbers are indexed at 0
2238 		 * and valid until the PDU is emitted.
2239 		 */
2240 		pp->payload_number = MAX(pp->payload_number,
2241 			(tx_sdu->target_event * session->burst_number));
2242 
2243 		/* Get actual event for this payload number */
2244 		actual_event = pp->payload_number / session->burst_number;
2245 
2246 		/* Get group reference point for this PDU based on the actual
2247 		 * event being set. This might introduce some errors as the
2248 		 * group refernce point for future events could drift. However
2249 		 * as the time offset calculation requires an absolute value,
2250 		 * this seems to be the best candidate.
2251 		 */
2252 		if (actual_event > tx_sdu->target_event) {
2253 			actual_grp_ref_point = isoal_get_wrapped_time_us(tx_sdu->grp_ref_point,
2254 				((actual_event - tx_sdu->target_event) * session->iso_interval *
2255 					ISO_INT_UNIT_US));
2256 		}
2257 
2258 		/* Check if time stamp on packet is later than the group
2259 		 * reference point and adjust targets. This could happen if the
2260 		 * SDU has been time-stampped at the controller when received
2261 		 * via HCI.
2262 		 *
2263 		 * BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
2264 		 * 3.1 Time_Offset in framed PDUs :
2265 		 * The Time_Offset shall be a positive value.
2266 		 */
2267 		if (!isoal_get_time_diff(tx_sdu->time_stamp, actual_grp_ref_point, &time_diff) ||
2268 			time_diff == 0) {
2269 			/* Advance target to next event */
2270 			actual_event++;
2271 			actual_grp_ref_point = isoal_get_wrapped_time_us(actual_grp_ref_point,
2272 							session->iso_interval * ISO_INT_UNIT_US);
2273 
2274 			/* Set payload number */
2275 			pp->payload_number = actual_event * session->burst_number;
2276 		}
2277 
2278 		/* Calculate the time offset */
2279 		time_diff_valid = isoal_get_time_diff(tx_sdu->time_stamp,
2280 					actual_grp_ref_point, &time_diff);
2281 		LL_ASSERT(time_diff_valid);
2282 		LL_ASSERT(time_diff > 0);
2283 		/* Time difference must be less than the maximum possible
2284 		 * time-offset of 24-bits.
2285 		 */
2286 		LL_ASSERT(time_diff <= 0x00FFFFFF);
2287 		time_offset = time_diff;
2288 
2289 		/* Store timing info for TX Sync command */
2290 		session->tx_time_stamp = actual_grp_ref_point;
2291 		session->tx_time_offset = time_offset;
2292 
2293 		/* Reset PDU fragmentation count for this SDU */
2294 		pp->pdu_cnt = 0;
2295 	}
2296 
2297 	/* PDUs should be created until the SDU fragment has been fragmented or if
2298 	 * this is the last fragment of the SDU, until the required padding PDU(s)
2299 	 * are sent.
2300 	 */
2301 	while ((err == ISOAL_STATUS_OK) &&
2302 		((packet_available > 0) || padding_pdu || zero_length_sdu)) {
2303 		const isoal_status_t err_alloc = isoal_tx_allocate_pdu(source, tx_sdu);
2304 		struct isoal_pdu_produced  *pdu = &pp->pdu;
2305 
2306 		err |= err_alloc;
2307 
2308 		if (pp->pdu_state == BT_ISO_START) {
2309 			/* Start of a new SDU. Segmentation header and time-offset
2310 			 * should be inserted.
2311 			 */
2312 			err |= isoal_insert_seg_header_timeoffset(source,
2313 								false, false,
2314 								time_offset);
2315 			pp->pdu_state = BT_ISO_CONT;
2316 		} else if (!padding_pdu && pp->pdu_state == BT_ISO_CONT && pp->pdu_written == 0) {
2317 			/* Continuing an SDU in a new PDU. Segmentation header
2318 			 * alone should be inserted.
2319 			 */
2320 			err |= isoal_insert_seg_header_timeoffset(source,
2321 								true, false,
2322 								0);
2323 		}
2324 
2325 		/*
2326 		 * For this PDU we can only consume of packet, bounded by:
2327 		 *   - What can fit in the destination PDU.
2328 		 *   - What remains of the packet.
2329 		 */
2330 		const size_t consume_len = MIN(
2331 			packet_available,
2332 			pp->pdu_available
2333 		);
2334 
2335 		/* End of the SDU fragment has been reached when the last of the
2336 		 * SDU is packed into a PDU.
2337 		 */
2338 		bool end_of_sdu_frag = !padding_pdu &&
2339 				((consume_len > 0 && consume_len == packet_available) ||
2340 					zero_length_sdu);
2341 
2342 		if (consume_len > 0) {
2343 			err |= session->pdu_write(&pdu->contents,
2344 						  pp->pdu_written,
2345 						  sdu_payload,
2346 						  consume_len);
2347 			sdu_payload       += consume_len;
2348 			pp->pdu_written   += consume_len;
2349 			pp->pdu_available -= consume_len;
2350 			packet_available  -= consume_len;
2351 		}
2352 
2353 		if (end_of_sdu_frag) {
2354 			/* Each PDU will carry the number of completed SDU
2355 			 * fragments contained in that PDU.
2356 			 */
2357 			pp->sdu_fragments++;
2358 		}
2359 
2360 		/* End of the SDU is reached at the end of the last SDU fragment
2361 		 * or if this is a single fragment SDU
2362 		 */
2363 		bool end_of_sdu = (packet_available == 0) &&
2364 				((tx_sdu->sdu_state == BT_ISO_SINGLE) ||
2365 					(tx_sdu->sdu_state == BT_ISO_END));
2366 		/* Update complete flag in last segmentation header */
2367 		err |= isoal_update_seg_header_cmplt_length(source, end_of_sdu, consume_len);
2368 
2369 		/* LLID is fixed for framed PDUs */
2370 		ll_id = PDU_BIS_LLID_FRAMED;
2371 
2372 		/* If there isn't sufficient usable space then release the
2373 		 * PDU when the end of the SDU is reached, instead of waiting
2374 		 * for the next SDU.
2375 		 */
2376 		bool release_pdu = end_of_sdu && (pp->pdu_available <= ISOAL_TX_SEGMENT_MIN_SIZE);
2377 		const isoal_status_t err_emit = isoal_tx_try_emit_pdu(source, release_pdu, ll_id);
2378 
2379 		err |= err_emit;
2380 
2381 		/* BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
2382 		 * 2 ISOAL Features :
2383 		 * Padding is required when the data does not add up to the
2384 		 * configured number of PDUs that are specified in the BN
2385 		 * parameter per CIS or BIS event.
2386 		 *
2387 		 * When padding PDUs as opposed to null PDUs are required for
2388 		 * framed production is not clear. Padding PDUs will be released
2389 		 * on the next event prepare trigger.
2390 		 */
2391 		padding_pdu = false;
2392 		zero_length_sdu = false;
2393 	}
2394 
2395 	return err;
2396 }
2397 
2398 /**
2399  * @brief  Handle preparation of the given source before commencing TX on the
2400  *         specified event (only for framed sources)
2401  * @param  source_hdl   Handle of source to prepare
2402  * @param  event_count  Event number source should be prepared for
2403  * @return              Status of operation
2404  */
isoal_tx_framed_event_prepare_handle(isoal_source_handle_t source_hdl,uint64_t event_count)2405 static isoal_status_t isoal_tx_framed_event_prepare_handle(isoal_source_handle_t source_hdl,
2406 							   uint64_t event_count)
2407 {
2408 	struct isoal_source_session *session;
2409 	struct isoal_pdu_production *pp;
2410 	uint64_t first_event_payload;
2411 	struct isoal_source *source;
2412 	uint64_t last_event_payload;
2413 	isoal_status_t err_alloc;
2414 	bool release_padding;
2415 	isoal_status_t err;
2416 
2417 	err = ISOAL_STATUS_OK;
2418 	err_alloc = ISOAL_STATUS_OK;
2419 	release_padding = false;
2420 
2421 	source = &isoal_global.source_state[source_hdl];
2422 	session = &source->session;
2423 	pp = &source->pdu_production;
2424 	first_event_payload = (session->burst_number * event_count);
2425 	last_event_payload = (session->burst_number * (event_count + 1ULL)) - 1ULL;
2426 
2427 	if (pp->pdu_available > 0 &&
2428 		pp->payload_number <= last_event_payload) {
2429 		/* Pending PDU that should be released for framed TX */
2430 		err = isoal_tx_try_emit_pdu(source, true, PDU_BIS_LLID_FRAMED);
2431 	}
2432 
2433 	if (pp->mode != ISOAL_PRODUCTION_MODE_DISABLED) {
2434 		/* BT Core V5.3 : Vol 6 Low Energy Controller :
2435 		 * Part G IS0-AL:
2436 		 *
2437 		 * 2 ISOAL Features :
2438 		 * Padding is required when the data does not add up to the
2439 		 * configured number of PDUs that are specified in the BN
2440 		 * parameter per CIS or BIS event.
2441 		 *
2442 		 * There is some lack of clarity in the specifications as to why
2443 		 * padding PDUs should be used as opposed to null PDUs. However
2444 		 * if a payload is not available, the LL must default to waiting
2445 		 * for the flush timeout before it can proceed to the next
2446 		 * payload.
2447 		 *
2448 		 * This means a loss of retransmission capacity for future
2449 		 * payloads that could exist. Sending padding PDUs will prevent
2450 		 * this loss while not resulting in additional SDUs on the
2451 		 * receiver. However it does incur the allocation and handling
2452 		 * overhead on the transmitter.
2453 		 *
2454 		 * As an interpretation of the specification, padding PDUs will
2455 		 * only be released if an SDU has been received in the current
2456 		 * event.
2457 		 */
2458 		if (pp->payload_number > first_event_payload) {
2459 			release_padding = true;
2460 		}
2461 	}
2462 
2463 	if (release_padding) {
2464 		while (!err && !err_alloc && (pp->payload_number < last_event_payload + 1ULL)) {
2465 			err_alloc = isoal_tx_allocate_pdu(source, NULL);
2466 
2467 			err = isoal_tx_try_emit_pdu(source, true, PDU_BIS_LLID_FRAMED);
2468 		}
2469 	}
2470 
2471 	/* Not possible to recover if allocation or emit fails here*/
2472 	LL_ASSERT(!(err || err_alloc));
2473 
2474 	if (pp->payload_number < last_event_payload + 1ULL) {
2475 		pp->payload_number = last_event_payload + 1ULL;
2476 	}
2477 
2478 	return err;
2479 }
2480 
2481 /**
2482  * @brief Deep copy a SDU, fragment into PDU(s)
2483  * @details Fragmentation will occur individually for every enabled source
2484  *
2485  * @param source_hdl[in] Handle of destination source
2486  * @param tx_sdu[in]     SDU along with packet boudary state
2487  * @return Status
2488  */
isoal_tx_sdu_fragment(isoal_source_handle_t source_hdl,struct isoal_sdu_tx * tx_sdu)2489 isoal_status_t isoal_tx_sdu_fragment(isoal_source_handle_t source_hdl,
2490 				     struct isoal_sdu_tx *tx_sdu)
2491 {
2492 	struct isoal_source_session *session;
2493 	struct isoal_source *source;
2494 	isoal_status_t err;
2495 
2496 	source = &isoal_global.source_state[source_hdl];
2497 	session = &source->session;
2498 	err = ISOAL_STATUS_ERR_PDU_ALLOC;
2499 
2500 	/* Set source context active to mutually exclude ISO Event prepare
2501 	 * kick.
2502 	 */
2503 	source->context_active = true;
2504 
2505 	if (source->pdu_production.mode != ISOAL_PRODUCTION_MODE_DISABLED) {
2506 		/* BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
2507 		 * 2 ISOAL Features :
2508 		 * (1) Unframed PDUs shall only be used when the ISO_Interval
2509 		 *     is equal to or an integer multiple of the SDU_Interval
2510 		 *     and a constant time offset alignment is maintained
2511 		 *     between the SDU generation and the timing in the
2512 		 *     isochronous transport.
2513 		 * (2) When the Host requests the use of framed PDUs, the
2514 		 *     Controller shall use framed PDUs.
2515 		 */
2516 		if (source->session.framed) {
2517 			err = isoal_tx_framed_produce(source_hdl, tx_sdu);
2518 		} else {
2519 			err = isoal_tx_unframed_produce(source_hdl, tx_sdu);
2520 		}
2521 	}
2522 
2523 	source->context_active = false;
2524 
2525 	if (source->timeout_trigger) {
2526 		source->timeout_trigger = false;
2527 		if (session->framed) {
2528 			isoal_tx_framed_event_prepare_handle(source_hdl,
2529 						source->timeout_event_count);
2530 		}
2531 	}
2532 
2533 	return err;
2534 }
2535 
isoal_tx_pdu_release(isoal_source_handle_t source_hdl,struct node_tx_iso * node_tx)2536 void isoal_tx_pdu_release(isoal_source_handle_t source_hdl,
2537 			  struct node_tx_iso *node_tx)
2538 {
2539 	struct isoal_source *source = &isoal_global.source_state[source_hdl];
2540 
2541 	if (source && source->session.pdu_release) {
2542 		source->session.pdu_release(node_tx, source->session.handle,
2543 					    ISOAL_STATUS_OK);
2544 	}
2545 }
2546 
2547 /**
2548  * @brief  Get information required for HCI_LE_Read_ISO_TX_Sync
2549  * @param  source_hdl Source handle linked to handle provided in HCI message
2550  * @param  seq        Packet Sequence number of last SDU
2551  * @param  timestamp  CIG / BIG reference point of last SDU
2552  * @param  offset     Time-offset (Framed) / 0 (Unframed) of last SDU
2553  * @return            Operation status
2554  */
isoal_tx_get_sync_info(isoal_source_handle_t source_hdl,uint16_t * seq,uint32_t * timestamp,uint32_t * offset)2555 isoal_status_t isoal_tx_get_sync_info(isoal_source_handle_t source_hdl,
2556 				      uint16_t *seq,
2557 				      uint32_t *timestamp,
2558 				      uint32_t *offset)
2559 {
2560 	if (isoal_check_source_hdl_valid(source_hdl) == ISOAL_STATUS_OK) {
2561 		struct isoal_source_session *session;
2562 
2563 		session = &isoal_global.source_state[source_hdl].session;
2564 
2565 		/* BT Core V5.3 : Vol 4 HCI : Part E HCI Functional Spec:
2566 		 * 7.8.96 LE Read ISO TX Sync Command:
2567 		 * If the Host issues this command before an SDU had been transmitted by
2568 		 * the Controller, then Controller shall return the error code Command
2569 		 * Disallowed.
2570 		 */
2571 		if (session->sn > 0) {
2572 			*seq = session->sn;
2573 			*timestamp = session->tx_time_stamp;
2574 			*offset = session->tx_time_offset;
2575 			return ISOAL_STATUS_OK;
2576 		}
2577 	}
2578 
2579 	return ISOAL_STATUS_ERR_UNSPECIFIED;
2580 }
2581 
2582 /**
2583  * @brief  Incoming prepare request before commencing TX for the specified
2584  *         event
2585  * @param  source_hdl   Handle of source to prepare
2586  * @param  event_count  Event number source should be prepared for
2587  * @return              Status of operation
2588  */
isoal_tx_event_prepare(isoal_source_handle_t source_hdl,uint64_t event_count)2589 void isoal_tx_event_prepare(isoal_source_handle_t source_hdl,
2590 			    uint64_t event_count)
2591 {
2592 	struct isoal_source_session *session;
2593 	struct isoal_source *source;
2594 
2595 	source = &isoal_global.source_state[source_hdl];
2596 	session = &source->session;
2597 
2598 	/* Store prepare timeout information and check if fragmentation context
2599 	 * is active.
2600 	 */
2601 	source->timeout_event_count = event_count;
2602 	source->timeout_trigger = true;
2603 	if (source->context_active) {
2604 		return;
2605 	}
2606 	source->timeout_trigger = false;
2607 
2608 	if (session->framed) {
2609 		isoal_tx_framed_event_prepare_handle(source_hdl, event_count);
2610 	}
2611 }
2612 
2613 #endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
2614