1 /*
2  * Copyright (c) 2021 Demant
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdint.h>
8 #include <zephyr/types.h>
9 #include <toolchain.h>
10 
11 /** Function return error codes */
12 typedef uint8_t isoal_status_t;
13 #define ISOAL_STATUS_OK                   ((isoal_status_t) 0x00) /* No error */
14 #define ISOAL_STATUS_ERR_SINK_ALLOC       ((isoal_status_t) 0x01) /* Sink pool full */
15 #define ISOAL_STATUS_ERR_SOURCE_ALLOC     ((isoal_status_t) 0x02) /* Source pool full */
16 #define ISOAL_STATUS_ERR_SDU_ALLOC        ((isoal_status_t) 0x04) /* SDU allocation */
17 #define ISOAL_STATUS_ERR_SDU_EMIT         ((isoal_status_t) 0x08) /* SDU emission */
18 
19 /** Handle to a registered ISO Sub-System sink */
20 typedef uint8_t  isoal_sink_handle_t;
21 
22 /** Byte length of an ISO SDU */
23 typedef uint16_t isoal_sdu_len_t;
24 
25 /** Byte length of an ISO PDU */
26 typedef uint8_t isoal_pdu_len_t;
27 
28 /** Count (ID number) of an ISO SDU */
29 typedef uint16_t isoal_sdu_cnt_t;
30 
31 /** Count (ID number) of an ISO PDU */
32 typedef uint64_t isoal_pdu_cnt_t;
33 
34 /** Ticks. Used for timestamp */
35 typedef uint32_t isoal_time_t;
36 
37 /** SDU status codes */
38 typedef uint8_t isoal_sdu_status_t;
39 #define ISOAL_SDU_STATUS_VALID          ((isoal_sdu_status_t) 0x00)
40 #define ISOAL_SDU_STATUS_ERRORS         ((isoal_sdu_status_t) 0x01)
41 #define ISOAL_SDU_STATUS_LOST_DATA      ((isoal_sdu_status_t) 0x02)
42 
43 /** PDU status codes */
44 typedef uint8_t isoal_pdu_status_t;
45 #define ISOAL_PDU_STATUS_VALID         ((isoal_pdu_status_t) 0x00)
46 #define ISOAL_PDU_STATUS_LOST_DATA     ((isoal_pdu_status_t) 0x01)
47 #define ISOAL_PDU_STATUS_ERRORS        ((isoal_pdu_status_t) 0x02)
48 
49 /** Production mode */
50 typedef uint8_t isoal_production_mode_t;
51 #define ISOAL_PRODUCTION_MODE_DISABLED    ((isoal_production_mode_t) 0x00)
52 #define ISOAL_PRODUCTION_MODE_ENABLED     ((isoal_production_mode_t) 0x01)
53 
54 
55 /**
56  *  Origin of {PDU or SDU}.
57  */
58 struct isoal_rx_origin {
59 	/** Originating subsystem */
60 	enum __packed {
61 		ISOAL_SUBSYS_BT_LLL   = 0x01,    /*!< Bluetooth LLL */
62 		ISOAL_SUBSYS_BT_HCI   = 0x02,    /*!< Bluetooth HCI */
63 		ISOAL_SUBSYS_VS       = 0xff     /*!< Vendor specific */
64 	} subsys;
65 
66 	/** Subsystem instance */
67 	union {
68 		struct {
69 			uint16_t  handle;       /*!< BT connection handle */
70 		} bt_lll;
71 	} inst;
72 };
73 
74 /**
75  * @brief ISO frame SDU buffer - typically an Audio frame buffer
76  *
77  * This provides the underlying vehicle of ISO SDU interchange through the
78  * ISO socket, the contents of which is generally an array of encoded bytes.
79  * Access and life time of this should be limited to ISO and the ISO socket.
80  * We assume no byte-fractional code words.
81  *
82  * Decoding code words to samples is the responsibility of the ISO sub system.
83  */
84 struct isoal_sdu_buffer {
85 	/** Code word buffer
86 	 *  Type, location and alignment decided by ISO sub system
87 	 */
88 	void *dbuf;
89 	/** Number of bytes accessible behind the dbuf pointer */
90 	isoal_sdu_len_t  size;
91 };
92 
93 /** @brief Produced ISO SDU frame with associated meta data */
94 struct isoal_sdu_produced {
95 	/** Status of contents, if valid or SDU was lost */
96 	isoal_sdu_status_t      status;
97 	/** Regardless of status, we always have timing */
98 	isoal_time_t            timestamp;
99 	/** Sequence number of SDU */
100 	isoal_sdu_cnt_t         seqn;
101 	/** Regardless of status, we always know where the PDUs that produced
102 	 *  this SDU, came from
103 	 */
104 	struct isoal_rx_origin  origin;
105 	/** Contents and length can only be trusted if status is valid */
106 	struct isoal_sdu_buffer contents;
107 	/** Optional context to be carried from PDU at alloc-time */
108 	void                    *ctx;
109 };
110 
111 /** @brief ISO PDU. Covers both CIS and BIS */
112 union isoal_pdu {
113 	struct pdu_cis cis;
114 	struct pdu_bis bis;
115 };
116 
117 
118 /** @brief Received ISO PDU with associated meta data */
119 struct isoal_pdu_rx {
120 	/** Meta */
121 	struct node_rx_iso_meta  *meta;
122 	/** PDU contents and length can only be trusted if status is valid */
123 	union isoal_pdu          *pdu;
124 };
125 
126 
127 /* Forward declaration */
128 struct isoal_sink;
129 
130 /**
131  * @brief  Callback: Request memory for a new ISO SDU buffer
132  *
133  * Proprietary ISO sub systems may have
134  * specific requirements or opinions on where to locate ISO SDUs; some
135  * memories may be faster, may be dynamically mapped in, etc.
136  *
137  * @return ISOAL_STATUS_ERR_ALLOC if size_request could not be fulfilled, otherwise
138  *         ISOAL_STATUS_OK.
139  */
140 typedef isoal_status_t (*isoal_sink_sdu_alloc_cb)(
141 	/*!< [in]  Sink context */
142 	const struct isoal_sink    *sink_ctx,
143 	/*!< [in]  Received PDU */
144 	const struct isoal_pdu_rx  *valid_pdu,
145 	/*!< [out] Struct is modified. Must not be NULL */
146 	struct isoal_sdu_buffer    *sdu_buffer
147 );
148 
149 /**
150  * @brief  Callback: Push an ISO SDU into an ISO sink
151  *
152  * Call also handing back buffer ownership
153  */
154 typedef isoal_status_t (*isoal_sink_sdu_emit_cb)(
155 	/*!< [in]  Sink context */
156 	const struct isoal_sink         *sink_ctx,
157 	/*!< [in]  Filled valid SDU to be pushed */
158 	const struct isoal_sdu_produced *valid_sdu
159 );
160 
161 /**
162  * @brief  Callback: Write a number of bytes to SDU buffer
163  */
164 typedef isoal_status_t (*isoal_sink_sdu_write_cb)(
165 	/*!< [in]  Destination buffer */
166 	void          *dbuf,
167 	/*!< [in]  Source data */
168 	const uint8_t *pdu_payload,
169 	/*!< [in]  Number of bytes to be copied */
170 	const size_t consume_len
171 );
172 
173 
174 struct isoal_sink_config {
175 	enum {
176 		ISOAL_MODE_CIS,
177 		ISOAL_MODE_BIS
178 	} mode;
179 	/* TODO add SDU and PDU max length etc. */
180 };
181 
182 
183 struct isoal_sink {
184 	/* Session-constant */
185 	struct {
186 		isoal_sink_sdu_alloc_cb  sdu_alloc;
187 		isoal_sink_sdu_emit_cb   sdu_emit;
188 		isoal_sink_sdu_write_cb  sdu_write;
189 		struct isoal_sink_config param;
190 		isoal_sdu_cnt_t          seqn;
191 		uint16_t                 handle;
192 		uint8_t                  pdus_per_sdu;
193 	} session;
194 
195 	/* State for SDU production */
196 	struct {
197 		/* Permit atomic enable/disable of SDU production */
198 		volatile isoal_production_mode_t  mode;
199 		/* We are constructing an SDU from {<1 or =1 or >1} PDUs */
200 		struct isoal_sdu_produced  sdu;
201 		/* Bookkeeping */
202 		isoal_pdu_cnt_t  prev_pdu_id : 39;
203 		enum {
204 			ISOAL_START,
205 			ISOAL_CONTINUE,
206 			ISOAL_ERR_SPOOL
207 		} fsm;
208 		uint8_t pdu_cnt;
209 		uint8_t sdu_state;
210 		isoal_sdu_len_t sdu_written;
211 		isoal_sdu_len_t sdu_available;
212 		isoal_sdu_status_t sdu_status;
213 	} sdu_production;
214 };
215 
216 
217 isoal_status_t isoal_init(void);
218 
219 isoal_status_t isoal_reset(void);
220 
221 isoal_status_t isoal_sink_create(isoal_sink_handle_t *hdl,
222 				 uint16_t handle,
223 				 uint8_t  burst_number,
224 				 uint32_t sdu_interval,
225 				 uint16_t iso_interval,
226 				 isoal_sink_sdu_alloc_cb sdu_alloc,
227 				 isoal_sink_sdu_emit_cb  sdu_emit,
228 				 isoal_sink_sdu_write_cb  sdu_write);
229 
230 struct isoal_sink_config *isoal_get_sink_param_ref(isoal_sink_handle_t hdl);
231 
232 void isoal_sink_enable(isoal_sink_handle_t hdl);
233 
234 void isoal_sink_disable(isoal_sink_handle_t hdl);
235 
236 void isoal_sink_destroy(isoal_sink_handle_t hdl);
237 
238 isoal_status_t isoal_rx_pdu_recombine(isoal_sink_handle_t sink_hdl,
239 				      const struct isoal_pdu_rx *pdu_meta);
240 
241 /* SDU Call backs for HCI interface  */
242 isoal_status_t sink_sdu_alloc_hci(const struct isoal_sink    *sink_ctx,
243 				  const struct isoal_pdu_rx  *valid_pdu,
244 				  struct isoal_sdu_buffer    *sdu_buffer);
245 isoal_status_t sink_sdu_emit_hci(const struct isoal_sink         *sink_ctx,
246 				 const struct isoal_sdu_produced *valid_sdu);
247 isoal_status_t sink_sdu_write_hci(void *dbuf,
248 				  const uint8_t *pdu_payload,
249 				  const size_t consume_len);
250