blob: 1b4993cb36b1e2058c135a3c958148dbbdb892ef [file] [log] [blame]
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001/* Bluetooth Audio Broadcast Source */
2
3/*
Emil Gydesen7fc05192023-02-20 14:33:43 +01004 * Copyright (c) 2021-2023 Nordic Semiconductor ASA
Emil Gydesenab87e0a2022-01-10 15:06:08 +01005 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
Gerard Marull-Paretas79e6b0e2022-08-25 09:58:46 +02009#include <zephyr/kernel.h>
Gerard Marull-Paretas5113c142022-05-06 11:12:04 +020010#include <zephyr/sys/byteorder.h>
11#include <zephyr/sys/check.h>
Emil Gydesenab87e0a2022-01-10 15:06:08 +010012
Gerard Marull-Paretas5113c142022-05-06 11:12:04 +020013#include <zephyr/bluetooth/bluetooth.h>
14#include <zephyr/bluetooth/conn.h>
15#include <zephyr/bluetooth/gatt.h>
16#include <zephyr/bluetooth/audio/audio.h>
Emil Gydesen77853f42023-02-27 15:32:52 +010017#include <zephyr/bluetooth/audio/bap.h>
Emil Gydesenab87e0a2022-01-10 15:06:08 +010018
Théo Battrele458f5a2022-11-02 14:31:13 +010019#include <zephyr/logging/log.h>
Emil Gydesenbe424292023-02-25 16:47:19 +010020LOG_MODULE_REGISTER(bt_bap_broadcast_source, CONFIG_BT_BAP_BROADCAST_SOURCE_LOG_LEVEL);
Emil Gydesenab87e0a2022-01-10 15:06:08 +010021
Emil Gydesen7fc05192023-02-20 14:33:43 +010022#include "bap_iso.h"
23#include "bap_endpoint.h"
Emil Gydesenab87e0a2022-01-10 15:06:08 +010024
Emil Gydesen099c1382023-03-01 11:57:11 +010025struct bt_bap_broadcast_subgroup {
Emil Gydesen40e39302022-09-05 11:29:06 +020026 /* The streams used to create the broadcast source */
27 sys_slist_t streams;
28
29 /* The codec of the subgroup */
Emil Gydesen69f7fd92023-05-16 14:16:14 +020030 struct bt_audio_codec_cfg *codec_cfg;
Emil Gydesen40e39302022-09-05 11:29:06 +020031
32 /* List node */
33 sys_snode_t _node;
34};
35
Emil Gydesen33268632023-03-01 11:18:25 +010036static struct bt_bap_ep broadcast_source_eps[CONFIG_BT_BAP_BROADCAST_SRC_COUNT]
37 [BROADCAST_STREAM_CNT];
Emil Gydesen099c1382023-03-01 11:57:11 +010038static struct bt_bap_broadcast_subgroup
Emil Gydesenbe424292023-02-25 16:47:19 +010039 broadcast_source_subgroups[CONFIG_BT_BAP_BROADCAST_SRC_COUNT]
40 [CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT];
41static struct bt_bap_broadcast_source broadcast_sources[CONFIG_BT_BAP_BROADCAST_SRC_COUNT];
Emil Gydesenab87e0a2022-01-10 15:06:08 +010042
Emil Gydesen40e39302022-09-05 11:29:06 +020043/**
44 * 2 octets UUID
45 * 3 octets presentation delay
46 * 1 octet number of subgroups
47 *
48 * Each subgroup then has
49 * 1 octet of number of BIS
50 * 5 octets of Codec_ID
51 * 1 octet codec specific configuration len
52 * 0-n octets of codec specific configuration
53 * 1 octet metadata len
54 * 0-n octets of metadata
55 *
56 * For each BIS in the subgroup there is
57 * 1 octet for the BIS index
58 * 1 octet codec specific configuration len
59 * 0-n octets of codec specific configuration
60 *
61 * For a minimal BASE with 1 subgroup and 1 BIS without and other data the
62 * total comes to 16
63 */
64#define MINIMUM_BASE_SIZE 16
65
Emil Gydesen33268632023-03-01 11:18:25 +010066static void broadcast_source_set_ep_state(struct bt_bap_ep *ep, uint8_t state)
Emil Gydesenab87e0a2022-01-10 15:06:08 +010067{
68 uint8_t old_state;
69
70 old_state = ep->status.state;
71
Emil Gydesen33268632023-03-01 11:18:25 +010072 LOG_DBG("ep %p id 0x%02x %s -> %s", ep, ep->status.id, bt_bap_ep_state_str(old_state),
73 bt_bap_ep_state_str(state));
Emil Gydesenab87e0a2022-01-10 15:06:08 +010074
75 switch (old_state) {
Emil Gydesen33268632023-03-01 11:18:25 +010076 case BT_BAP_EP_STATE_IDLE:
77 if (state != BT_BAP_EP_STATE_QOS_CONFIGURED) {
Théo Battrele458f5a2022-11-02 14:31:13 +010078 LOG_DBG("Invalid broadcast sync endpoint state transition");
Emil Gydesenab87e0a2022-01-10 15:06:08 +010079 return;
80 }
81 break;
Emil Gydesen33268632023-03-01 11:18:25 +010082 case BT_BAP_EP_STATE_QOS_CONFIGURED:
83 if (state != BT_BAP_EP_STATE_IDLE && state != BT_BAP_EP_STATE_ENABLING) {
Théo Battrele458f5a2022-11-02 14:31:13 +010084 LOG_DBG("Invalid broadcast sync endpoint state transition");
Emil Gydesen05ace182022-06-03 10:48:19 +020085 return;
86 }
87 break;
Emil Gydesen33268632023-03-01 11:18:25 +010088 case BT_BAP_EP_STATE_ENABLING:
Emil Gydesen590d3e12024-02-05 15:46:22 +010089 if (state != BT_BAP_EP_STATE_STREAMING && state != BT_BAP_EP_STATE_QOS_CONFIGURED) {
Théo Battrele458f5a2022-11-02 14:31:13 +010090 LOG_DBG("Invalid broadcast sync endpoint state transition");
Emil Gydesenab87e0a2022-01-10 15:06:08 +010091 return;
92 }
Emil Gydesen2df90772022-03-18 13:53:31 +010093 break;
Emil Gydesen33268632023-03-01 11:18:25 +010094 case BT_BAP_EP_STATE_STREAMING:
95 if (state != BT_BAP_EP_STATE_QOS_CONFIGURED) {
Théo Battrele458f5a2022-11-02 14:31:13 +010096 LOG_DBG("Invalid broadcast sync endpoint state transition");
Emil Gydesenab87e0a2022-01-10 15:06:08 +010097 return;
98 }
99 break;
100 default:
Théo Battrele458f5a2022-11-02 14:31:13 +0100101 LOG_ERR("Invalid broadcast sync endpoint state: %s",
Emil Gydesen33268632023-03-01 11:18:25 +0100102 bt_bap_ep_state_str(old_state));
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100103 return;
104 }
105
106 ep->status.state = state;
Emil Gydesenc66f2102023-05-09 13:51:02 +0200107}
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100108
Emil Gydesenc66f2102023-05-09 13:51:02 +0200109static void broadcast_source_set_state(struct bt_bap_broadcast_source *source, uint8_t state)
110{
111 struct bt_bap_broadcast_subgroup *subgroup;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100112
Emil Gydesenc66f2102023-05-09 13:51:02 +0200113 SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
114 struct bt_bap_stream *stream;
115
116 SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
117 broadcast_source_set_ep_state(stream->ep, state);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100118 }
119 }
120}
121
Emil Gydesen4518c192022-03-07 14:18:26 +0100122static void broadcast_source_iso_sent(struct bt_iso_chan *chan)
123{
Emil Gydesene2765d72023-03-01 11:52:29 +0100124 struct bt_bap_iso *iso = CONTAINER_OF(chan, struct bt_bap_iso, chan);
Emil Gydesen77853f42023-02-27 15:32:52 +0100125 const struct bt_bap_stream_ops *ops;
126 struct bt_bap_stream *stream;
Emil Gydesen33268632023-03-01 11:18:25 +0100127 struct bt_bap_ep *ep = iso->tx.ep;
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200128
129 if (ep == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100130 LOG_ERR("iso %p not bound with ep", chan);
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200131 return;
132 }
133
134 stream = ep->stream;
135 if (stream == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100136 LOG_ERR("No stream for ep %p", ep);
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200137 return;
138 }
139
140 ops = stream->ops;
Emil Gydesen4518c192022-03-07 14:18:26 +0100141
Emil Gydesen77853f42023-02-27 15:32:52 +0100142 if (IS_ENABLED(CONFIG_BT_BAP_DEBUG_STREAM_DATA)) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100143 LOG_DBG("stream %p ep %p", stream, stream->ep);
Emil Gydesen65f130b2022-09-26 17:15:24 +0200144 }
Emil Gydesen4518c192022-03-07 14:18:26 +0100145
146 if (ops != NULL && ops->sent != NULL) {
Emil Gydesen9b154262022-05-24 13:59:45 +0200147 ops->sent(stream);
Emil Gydesen4518c192022-03-07 14:18:26 +0100148 }
149}
150
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100151static void broadcast_source_iso_connected(struct bt_iso_chan *chan)
152{
Emil Gydesene2765d72023-03-01 11:52:29 +0100153 struct bt_bap_iso *iso = CONTAINER_OF(chan, struct bt_bap_iso, chan);
Emil Gydesen77853f42023-02-27 15:32:52 +0100154 const struct bt_bap_stream_ops *ops;
155 struct bt_bap_stream *stream;
Emil Gydesen33268632023-03-01 11:18:25 +0100156 struct bt_bap_ep *ep = iso->tx.ep;
Emil Gydesene8e19202022-02-14 14:18:03 +0100157
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200158 if (ep == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100159 LOG_ERR("iso %p not bound with ep", chan);
Emil Gydesen9b154262022-05-24 13:59:45 +0200160 return;
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200161 }
162
163 stream = ep->stream;
164 if (stream == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100165 LOG_ERR("No stream for ep %p", ep);
Emil Gydesene8e19202022-02-14 14:18:03 +0100166 return;
167 }
168
Théo Battrele458f5a2022-11-02 14:31:13 +0100169 LOG_DBG("stream %p ep %p", stream, ep);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100170
Emil Gydesen7cfc8b32024-02-08 13:39:33 +0100171#if defined(CONFIG_BT_BAP_DEBUG_STREAM_SEQ_NUM)
172 /* reset sequence number */
173 stream->_prev_seq_num = 0U;
174#endif /* CONFIG_BT_BAP_DEBUG_STREAM_SEQ_NUM */
175
Emil Gydesenb857ef72023-12-28 13:50:56 +0100176 ops = stream->ops;
177 if (ops != NULL && ops->connected != NULL) {
178 ops->connected(stream);
179 }
180
Emil Gydesen33268632023-03-01 11:18:25 +0100181 broadcast_source_set_ep_state(ep, BT_BAP_EP_STATE_STREAMING);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100182
Emil Gydesen628b54a2022-02-02 17:12:30 +0100183 if (ops != NULL && ops->started != NULL) {
Emil Gydesen9b154262022-05-24 13:59:45 +0200184 ops->started(stream);
Emil Gydesen102dcca2022-01-20 15:03:00 +0100185 } else {
Emil Gydesen68a4d0f2023-08-31 15:30:31 +0200186 LOG_WRN("No callback for started set");
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100187 }
188}
189
190static void broadcast_source_iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
191{
Emil Gydesene2765d72023-03-01 11:52:29 +0100192 struct bt_bap_iso *iso = CONTAINER_OF(chan, struct bt_bap_iso, chan);
Emil Gydesen77853f42023-02-27 15:32:52 +0100193 const struct bt_bap_stream_ops *ops;
194 struct bt_bap_stream *stream;
Emil Gydesen33268632023-03-01 11:18:25 +0100195 struct bt_bap_ep *ep = iso->tx.ep;
Emil Gydesene8e19202022-02-14 14:18:03 +0100196
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200197 if (ep == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100198 LOG_ERR("iso %p not bound with ep", chan);
Emil Gydesen9b154262022-05-24 13:59:45 +0200199 return;
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200200 }
201
202 stream = ep->stream;
203 if (stream == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100204 LOG_ERR("No stream for ep %p", ep);
Emil Gydesene8e19202022-02-14 14:18:03 +0100205 return;
206 }
207
Théo Battrele458f5a2022-11-02 14:31:13 +0100208 LOG_DBG("stream %p ep %p reason 0x%02x", stream, stream->ep, reason);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100209
Emil Gydesenb857ef72023-12-28 13:50:56 +0100210 ops = stream->ops;
211 if (ops != NULL && ops->disconnected != NULL) {
212 ops->disconnected(stream, reason);
213 }
214
Emil Gydesen33268632023-03-01 11:18:25 +0100215 broadcast_source_set_ep_state(ep, BT_BAP_EP_STATE_QOS_CONFIGURED);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100216
Emil Gydesen628b54a2022-02-02 17:12:30 +0100217 if (ops != NULL && ops->stopped != NULL) {
Emil Gydesen59a9f8a2023-03-02 15:01:00 +0100218 ops->stopped(stream, reason);
Emil Gydesen102dcca2022-01-20 15:03:00 +0100219 } else {
Théo Battrele458f5a2022-11-02 14:31:13 +0100220 LOG_WRN("No callback for stopped set");
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100221 }
222}
223
224static struct bt_iso_chan_ops broadcast_source_iso_ops = {
Emil Gydesen4518c192022-03-07 14:18:26 +0100225 .sent = broadcast_source_iso_sent,
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100226 .connected = broadcast_source_iso_connected,
227 .disconnected = broadcast_source_iso_disconnected,
228};
229
Emil Gydesen33268632023-03-01 11:18:25 +0100230bool bt_bap_ep_is_broadcast_src(const struct bt_bap_ep *ep)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100231{
232 for (int i = 0; i < ARRAY_SIZE(broadcast_source_eps); i++) {
233 if (PART_OF_ARRAY(broadcast_source_eps[i], ep)) {
234 return true;
235 }
236 }
237
238 return false;
239}
240
Emil Gydesen33268632023-03-01 11:18:25 +0100241static void broadcast_source_ep_init(struct bt_bap_ep *ep)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100242{
Théo Battrele458f5a2022-11-02 14:31:13 +0100243 LOG_DBG("ep %p", ep);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100244
245 (void)memset(ep, 0, sizeof(*ep));
Emil Gydesen6191a762022-03-29 18:20:10 +0200246 ep->dir = BT_AUDIO_DIR_SOURCE;
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200247 ep->iso = NULL;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100248}
249
Emil Gydesen33268632023-03-01 11:18:25 +0100250static struct bt_bap_ep *broadcast_source_new_ep(uint8_t index)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100251{
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100252 for (size_t i = 0; i < ARRAY_SIZE(broadcast_source_eps[index]); i++) {
Emil Gydesen33268632023-03-01 11:18:25 +0100253 struct bt_bap_ep *ep = &broadcast_source_eps[index][i];
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100254
255 /* If ep->stream is NULL the endpoint is unallocated */
256 if (ep->stream == NULL) {
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200257 broadcast_source_ep_init(ep);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100258 return ep;
259 }
260 }
261
262 return NULL;
263}
264
Emil Gydesen099c1382023-03-01 11:57:11 +0100265static struct bt_bap_broadcast_subgroup *broadcast_source_new_subgroup(uint8_t index)
Emil Gydesen40e39302022-09-05 11:29:06 +0200266{
267 for (size_t i = 0; i < ARRAY_SIZE(broadcast_source_subgroups[index]); i++) {
Emil Gydesen099c1382023-03-01 11:57:11 +0100268 struct bt_bap_broadcast_subgroup *subgroup = &broadcast_source_subgroups[index][i];
Emil Gydesen40e39302022-09-05 11:29:06 +0200269
270 if (sys_slist_is_empty(&subgroup->streams)) {
271 return subgroup;
272 }
273 }
274
275 return NULL;
276}
277
Emil Gydesen77853f42023-02-27 15:32:52 +0100278static int broadcast_source_setup_stream(uint8_t index, struct bt_bap_stream *stream,
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200279 struct bt_audio_codec_cfg *codec_cfg,
280 struct bt_audio_codec_qos *qos,
Emil Gydesenbe424292023-02-25 16:47:19 +0100281 struct bt_bap_broadcast_source *source)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100282{
Emil Gydesene2765d72023-03-01 11:52:29 +0100283 struct bt_bap_iso *iso;
Emil Gydesen33268632023-03-01 11:18:25 +0100284 struct bt_bap_ep *ep;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100285
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100286 ep = broadcast_source_new_ep(index);
287 if (ep == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100288 LOG_DBG("Could not allocate new broadcast endpoint");
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100289 return -ENOMEM;
290 }
291
Emil Gydesene2765d72023-03-01 11:52:29 +0100292 iso = bt_bap_iso_new();
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200293 if (iso == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100294 LOG_DBG("Could not allocate iso");
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200295 return -ENOMEM;
296 }
297
Emil Gydesene2765d72023-03-01 11:52:29 +0100298 bt_bap_iso_init(iso, &broadcast_source_iso_ops);
299 bt_bap_iso_bind_ep(iso, ep);
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200300
301 bt_audio_codec_qos_to_iso_qos(iso->chan.qos->tx, qos);
Emil Gydesenaef39f62024-01-19 13:13:08 +0100302 bt_bap_iso_configure_data_path(ep, codec_cfg);
Emil Gydesen84c01bb2023-01-23 14:33:54 +0100303#if defined(CONFIG_BT_ISO_TEST_PARAMS)
304 iso->chan.qos->num_subevents = qos->num_subevents;
305#endif /* CONFIG_BT_ISO_TEST_PARAMS */
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200306
Emil Gydesene2765d72023-03-01 11:52:29 +0100307 bt_bap_iso_unref(iso);
Mariusz Skamra3fa45692022-10-21 14:35:10 +0200308
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200309 bt_bap_stream_attach(NULL, stream, ep, codec_cfg);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100310 stream->qos = qos;
Emil Gydesen40e39302022-09-05 11:29:06 +0200311 ep->broadcast_source = source;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100312
313 return 0;
314}
315
Emil Gydesen099c1382023-03-01 11:57:11 +0100316static bool encode_base_subgroup(struct bt_bap_broadcast_subgroup *subgroup,
317 struct bt_audio_broadcast_stream_data *stream_data,
318 uint8_t *streams_encoded, struct net_buf_simple *buf)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100319{
Emil Gydesen77853f42023-02-27 15:32:52 +0100320 struct bt_bap_stream *stream;
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200321 const struct bt_audio_codec_cfg *codec_cfg;
Emil Gydesen40e39302022-09-05 11:29:06 +0200322 uint8_t stream_count;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100323 uint8_t len;
324
Emil Gydesen40e39302022-09-05 11:29:06 +0200325 stream_count = 0;
326 SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
327 stream_count++;
Emil Gydesen7ab1caf2022-09-02 15:43:30 +0200328 }
Emil Gydesen7ab1caf2022-09-02 15:43:30 +0200329
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200330 codec_cfg = subgroup->codec_cfg;
Emil Gydesen40e39302022-09-05 11:29:06 +0200331
332 net_buf_simple_add_u8(buf, stream_count);
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200333 net_buf_simple_add_u8(buf, codec_cfg->id);
334 net_buf_simple_add_le16(buf, codec_cfg->cid);
335 net_buf_simple_add_le16(buf, codec_cfg->vid);
Emil Gydesen40e39302022-09-05 11:29:06 +0200336
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200337 net_buf_simple_add_u8(buf, codec_cfg->data_len);
338#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0
339 if ((buf->size - buf->len) < codec_cfg->data_len) {
340 LOG_DBG("No room for config data: %zu", codec_cfg->data_len);
Emil Gydesen7ab1caf2022-09-02 15:43:30 +0200341
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200342 return false;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100343 }
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200344 net_buf_simple_add_mem(buf, codec_cfg->data, codec_cfg->data_len);
345#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100346
Emil Gydesen40e39302022-09-05 11:29:06 +0200347 if ((buf->size - buf->len) < sizeof(len)) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100348 LOG_DBG("No room for metadata length");
Emil Gydesen7ab1caf2022-09-02 15:43:30 +0200349
350 return false;
351 }
352
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200353 net_buf_simple_add_u8(buf, codec_cfg->meta_len);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100354
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200355#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0
356 if ((buf->size - buf->len) < codec_cfg->meta_len) {
357 LOG_DBG("No room for metadata data: %zu", codec_cfg->meta_len);
Emil Gydesen7ab1caf2022-09-02 15:43:30 +0200358
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200359 return false;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100360 }
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200361
362 net_buf_simple_add_mem(buf, codec_cfg->meta, codec_cfg->meta_len);
363#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0 */
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100364
365 /* Create BIS index bitfield */
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200366 for (uint8_t i = 0U; i < stream_count; i++) {
Emil Gydesenaf953a12023-10-04 23:31:28 +0200367 /* Set the bis_index to *streams_encoded plus 1 as the indexes start from 1 */
368 const uint8_t bis_index = *streams_encoded + 1;
369
Emil Gydesen40e39302022-09-05 11:29:06 +0200370 if ((buf->size - buf->len) < (sizeof(bis_index) + sizeof(uint8_t))) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100371 LOG_DBG("No room for BIS[%d] index", i);
Emil Gydesen7ab1caf2022-09-02 15:43:30 +0200372
373 return false;
374 }
375
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100376 net_buf_simple_add_u8(buf, bis_index);
Emil Gydesen40e39302022-09-05 11:29:06 +0200377
378 if ((buf->size - buf->len) < sizeof(len)) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100379 LOG_DBG("No room for bis codec config length");
Emil Gydesen40e39302022-09-05 11:29:06 +0200380
381 return false;
382 }
383
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200384 net_buf_simple_add_u8(buf, stream_data[i].data_len);
385#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0
386 if ((buf->size - buf->len) < stream_data[i].data_len) {
387 LOG_DBG("No room for BIS[%u] data: %zu", i, stream_data[i].data_len);
Emil Gydesen40e39302022-09-05 11:29:06 +0200388
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200389 return false;
Emil Gydesen40e39302022-09-05 11:29:06 +0200390 }
Emil Gydesen40e39302022-09-05 11:29:06 +0200391
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200392 net_buf_simple_add_mem(buf, stream_data[i].data, stream_data[i].data_len);
393#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */
Emil Gydesen40e39302022-09-05 11:29:06 +0200394
Emil Gydesenaf953a12023-10-04 23:31:28 +0200395 (*streams_encoded)++;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100396 }
397
Emil Gydesen40e39302022-09-05 11:29:06 +0200398 return true;
399}
400
Emil Gydesenbe424292023-02-25 16:47:19 +0100401static bool encode_base(struct bt_bap_broadcast_source *source, struct net_buf_simple *buf)
Emil Gydesen40e39302022-09-05 11:29:06 +0200402{
Emil Gydesen099c1382023-03-01 11:57:11 +0100403 struct bt_bap_broadcast_subgroup *subgroup;
Emil Gydesen40e39302022-09-05 11:29:06 +0200404 uint8_t streams_encoded;
405 uint8_t subgroup_count;
406
407 /* 13 is the size of the fixed size values following this check */
408 if ((buf->size - buf->len) < MINIMUM_BASE_SIZE) {
409 return false;
410 }
411
412 subgroup_count = 0U;
413 SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
414 subgroup_count++;
415 }
416
417 net_buf_simple_add_le16(buf, BT_UUID_BASIC_AUDIO_VAL);
418
419 net_buf_simple_add_le24(buf, source->qos->pd);
420 net_buf_simple_add_u8(buf, subgroup_count);
421
422 /* Since the `stream_data` is only stored in the broadcast source,
423 * we need to provide that information when encoding each subgroup
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100424 */
Emil Gydesen40e39302022-09-05 11:29:06 +0200425 streams_encoded = 0;
426 SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
427 if (!encode_base_subgroup(subgroup,
428 &source->stream_data[streams_encoded],
429 &streams_encoded, buf)) {
430 return false;
431 }
432 }
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100433
Emil Gydesen7ab1caf2022-09-02 15:43:30 +0200434 return true;
435}
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100436
Emil Gydesenbe424292023-02-25 16:47:19 +0100437static int generate_broadcast_id(struct bt_bap_broadcast_source *source)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100438{
439 bool unique;
440
441 do {
442 int err;
443
444 err = bt_rand(&source->broadcast_id,
445 BT_AUDIO_BROADCAST_ID_SIZE);
446 if (err) {
447 return err;
448 }
449
450 /* Ensure uniqueness */
451 unique = true;
452 for (int i = 0; i < ARRAY_SIZE(broadcast_sources); i++) {
453 if (&broadcast_sources[i] == source) {
454 continue;
455 }
456
457 if (broadcast_sources[i].broadcast_id == source->broadcast_id) {
458 unique = false;
459 break;
460 }
461 }
462 } while (!unique);
463
464 return 0;
465}
466
Emil Gydesenbe424292023-02-25 16:47:19 +0100467static void broadcast_source_cleanup(struct bt_bap_broadcast_source *source)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100468{
Emil Gydesen099c1382023-03-01 11:57:11 +0100469 struct bt_bap_broadcast_subgroup *subgroup, *next_subgroup;
Emil Gydesen5628b292022-03-11 16:20:31 +0100470
Emil Gydesen40e39302022-09-05 11:29:06 +0200471 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&source->subgroups, subgroup,
472 next_subgroup, _node) {
Emil Gydesen77853f42023-02-27 15:32:52 +0100473 struct bt_bap_stream *stream, *next_stream;
Emil Gydesen468bd4d2022-03-11 14:58:51 +0100474
Emil Gydesen40e39302022-09-05 11:29:06 +0200475 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subgroup->streams, stream,
476 next_stream, _node) {
Emil Gydesene2765d72023-03-01 11:52:29 +0100477 bt_bap_iso_unbind_ep(stream->ep->iso, stream->ep);
Emil Gydesen40e39302022-09-05 11:29:06 +0200478 stream->ep->stream = NULL;
479 stream->ep = NULL;
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200480 stream->codec_cfg = NULL;
Emil Gydesen40e39302022-09-05 11:29:06 +0200481 stream->qos = NULL;
482 stream->group = NULL;
483
484 sys_slist_remove(&subgroup->streams, NULL,
485 &stream->_node);
486 }
487 sys_slist_remove(&source->subgroups, NULL, &subgroup->_node);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100488 }
489
490 (void)memset(source, 0, sizeof(*source));
491}
492
Emil Gydesend327b162023-08-17 13:15:12 +0200493static bool valid_broadcast_source_param(const struct bt_bap_broadcast_source_param *param,
494 const struct bt_bap_broadcast_source *source)
Emil Gydesen40e39302022-09-05 11:29:06 +0200495{
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200496 const struct bt_audio_codec_qos *qos;
Emil Gydesen40e39302022-09-05 11:29:06 +0200497
498 CHECKIF(param == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100499 LOG_DBG("param is NULL");
Emil Gydesen40e39302022-09-05 11:29:06 +0200500 return false;
501 }
502
Emil Gydesen117f6292023-05-09 14:05:36 +0200503 CHECKIF(!IN_RANGE(param->params_count, 1U, CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT)) {
504 LOG_DBG("param->params_count %zu is invalid", param->params_count);
Emil Gydesen40e39302022-09-05 11:29:06 +0200505 return false;
506 }
507
Emil Gydesenba194972022-12-20 15:39:07 +0100508 CHECKIF(param->packing != BT_ISO_PACKING_SEQUENTIAL &&
509 param->packing != BT_ISO_PACKING_INTERLEAVED) {
510 LOG_DBG("param->packing %u is invalid", param->packing);
511 return false;
512 }
513
Emil Gydesen40e39302022-09-05 11:29:06 +0200514 qos = param->qos;
515 CHECKIF(qos == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100516 LOG_DBG("param->qos is NULL");
Emil Gydesen40e39302022-09-05 11:29:06 +0200517 return false;
518 }
519
Magdalena Kasenberg57784df2023-02-15 11:22:04 +0100520 CHECKIF(bt_audio_verify_qos(qos) != BT_BAP_ASCS_REASON_NONE) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100521 LOG_DBG("param->qos is invalid");
Emil Gydesen40e39302022-09-05 11:29:06 +0200522 return false;
523 }
524
Emil Gydesenfa95a7a2023-04-12 16:29:29 +0200525 CHECKIF(param->qos->rtn > BT_ISO_BROADCAST_RTN_MAX) {
526 LOG_DBG("param->qos->rtn %u invalid", param->qos->rtn);
527 return false;
528 }
529
530 CHECKIF(param->params == NULL) {
531 LOG_DBG("param->params is NULL");
532 return false;
533 }
534
535 CHECKIF(param->params_count == 0) {
536 LOG_DBG("param->params_count is 0");
537 return false;
538 }
539
Emil Gydesen40e39302022-09-05 11:29:06 +0200540 for (size_t i = 0U; i < param->params_count; i++) {
Emil Gydesenbe424292023-02-25 16:47:19 +0100541 const struct bt_bap_broadcast_source_subgroup_param *subgroup_param;
Emil Gydesen40e39302022-09-05 11:29:06 +0200542
543 subgroup_param = &param->params[i];
544
Emil Gydesenfa95a7a2023-04-12 16:29:29 +0200545 CHECKIF(subgroup_param->params == NULL) {
546 LOG_DBG("subgroup_params[%zu].params is NULL", i);
Emil Gydesen40e39302022-09-05 11:29:06 +0200547 return false;
548 }
549
Emil Gydesenfa95a7a2023-04-12 16:29:29 +0200550 CHECKIF(!IN_RANGE(subgroup_param->params_count, 1U,
551 CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT)) {
552 LOG_DBG("subgroup_params[%zu].count (%zu) is invalid", i,
553 subgroup_param->params_count);
554 return false;
555 }
556
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200557 CHECKIF(!bt_audio_valid_codec_cfg(subgroup_param->codec_cfg)) {
558 LOG_DBG("subgroup_params[%zu].codec_cfg is invalid", i);
Emil Gydesen40e39302022-09-05 11:29:06 +0200559 return false;
560 }
561
562 for (size_t j = 0U; j < subgroup_param->params_count; j++) {
Emil Gydesenbe424292023-02-25 16:47:19 +0100563 const struct bt_bap_broadcast_source_stream_param *stream_param;
Emil Gydesen40e39302022-09-05 11:29:06 +0200564
565 stream_param = &subgroup_param->params[j];
566
567 CHECKIF(stream_param->stream == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100568 LOG_DBG("subgroup_params[%zu].stream_params[%zu]->stream is NULL",
569 i, j);
Emil Gydesen40e39302022-09-05 11:29:06 +0200570 return false;
571 }
572
Emil Gydesend327b162023-08-17 13:15:12 +0200573 CHECKIF(stream_param->stream->group != NULL &&
574 stream_param->stream->group != source) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100575 LOG_DBG("subgroup_params[%zu].stream_params[%zu]->stream is "
576 "already part of group %p",
577 i, j, stream_param->stream->group);
Emil Gydesen40e39302022-09-05 11:29:06 +0200578 return false;
579 }
Emil Gydesenfa95a7a2023-04-12 16:29:29 +0200580
Emil Gydesen8a6c1d92024-02-23 15:02:41 +0100581#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200582 CHECKIF(stream_param->data == NULL && stream_param->data_len != 0) {
Emil Gydesenfa95a7a2023-04-12 16:29:29 +0200583 LOG_DBG("subgroup_params[%zu].stream_params[%zu]->data is "
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200584 "NULL with len %zu",
585 i, j, stream_param->data_len);
Emil Gydesenfa95a7a2023-04-12 16:29:29 +0200586 return false;
587 }
588
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200589 CHECKIF(stream_param->data_len > CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE) {
590 LOG_DBG("subgroup_params[%zu].stream_params[%zu]->data_len too "
591 "large: %zu > %d",
592 i, j, stream_param->data_len,
593 CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE);
Emil Gydesenfa95a7a2023-04-12 16:29:29 +0200594 return false;
595 }
Emil Gydesen8a6c1d92024-02-23 15:02:41 +0100596
597 CHECKIF(subgroup_param->codec_cfg->id == BT_HCI_CODING_FORMAT_LC3 &&
598 !bt_audio_valid_ltv(stream_param->data, stream_param->data_len)) {
599 LOG_DBG("subgroup_params[%zu].stream_params[%zu]->data not valid "
600 "LTV",
601 i, j);
602 return false;
603 }
Emil Gydesen40e39302022-09-05 11:29:06 +0200604 }
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200605#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */
Emil Gydesen40e39302022-09-05 11:29:06 +0200606 }
607
608 return true;
609}
610
Emil Gydesen33268632023-03-01 11:18:25 +0100611static enum bt_bap_ep_state broadcast_source_get_state(struct bt_bap_broadcast_source *source)
Emil Gydesen40e39302022-09-05 11:29:06 +0200612{
Emil Gydesen099c1382023-03-01 11:57:11 +0100613 struct bt_bap_broadcast_subgroup *subgroup;
Emil Gydesen77853f42023-02-27 15:32:52 +0100614 struct bt_bap_stream *stream;
Emil Gydesen40e39302022-09-05 11:29:06 +0200615 sys_snode_t *head_node;
616
617 if (source == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100618 LOG_DBG("source is NULL");
Emil Gydesen33268632023-03-01 11:18:25 +0100619 return BT_BAP_EP_STATE_IDLE;
Emil Gydesen40e39302022-09-05 11:29:06 +0200620 }
621
622 if (sys_slist_is_empty(&source->subgroups)) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100623 LOG_DBG("Source does not have any streams");
Emil Gydesen33268632023-03-01 11:18:25 +0100624 return BT_BAP_EP_STATE_IDLE;
Emil Gydesen40e39302022-09-05 11:29:06 +0200625 }
626
627 /* Get the first stream */
628 head_node = sys_slist_peek_head(&source->subgroups);
Emil Gydesen099c1382023-03-01 11:57:11 +0100629 subgroup = CONTAINER_OF(head_node, struct bt_bap_broadcast_subgroup, _node);
Emil Gydesen40e39302022-09-05 11:29:06 +0200630
631 head_node = sys_slist_peek_head(&subgroup->streams);
Emil Gydesen77853f42023-02-27 15:32:52 +0100632 stream = CONTAINER_OF(head_node, struct bt_bap_stream, _node);
Emil Gydesen40e39302022-09-05 11:29:06 +0200633
634 /* All streams in a broadcast source is in the same state,
635 * so we can just check the first stream
636 */
637 if (stream->ep == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100638 LOG_DBG("stream->ep is NULL");
Emil Gydesen33268632023-03-01 11:18:25 +0100639 return BT_BAP_EP_STATE_IDLE;
Emil Gydesen40e39302022-09-05 11:29:06 +0200640 }
641
642 return stream->ep->status.state;
643}
644
Emil Gydesend327b162023-08-17 13:15:12 +0200645int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param,
Emil Gydesenbe424292023-02-25 16:47:19 +0100646 struct bt_bap_broadcast_source **out_source)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100647{
Emil Gydesenbe424292023-02-25 16:47:19 +0100648 struct bt_bap_broadcast_source *source;
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200649 struct bt_audio_codec_qos *qos;
Emil Gydesen40e39302022-09-05 11:29:06 +0200650 size_t stream_count;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100651 uint8_t index;
652 int err;
653
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100654 CHECKIF(out_source == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100655 LOG_DBG("out_source is NULL");
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100656 return -EINVAL;
657 }
Emil Gydesenfa95a7a2023-04-12 16:29:29 +0200658
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100659 /* Set out_source to NULL until the source has actually been created */
660 *out_source = NULL;
661
Emil Gydesend327b162023-08-17 13:15:12 +0200662 if (!valid_broadcast_source_param(param, NULL)) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100663 LOG_DBG("Invalid parameters");
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100664 return -EINVAL;
665 }
666
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100667 source = NULL;
668 for (index = 0; index < ARRAY_SIZE(broadcast_sources); index++) {
Emil Gydesen40e39302022-09-05 11:29:06 +0200669 if (sys_slist_is_empty(&broadcast_sources[index].subgroups)) { /* Find free entry */
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100670 source = &broadcast_sources[index];
671 break;
672 }
673 }
674
675 if (source == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100676 LOG_DBG("Could not allocate any more broadcast sources");
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100677 return -ENOMEM;
678 }
679
Emil Gydesen40e39302022-09-05 11:29:06 +0200680 stream_count = 0U;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100681
Emil Gydesen40e39302022-09-05 11:29:06 +0200682 qos = param->qos;
683 /* Go through all subgroups and streams and setup each setup with an
684 * endpoint
685 */
686 for (size_t i = 0U; i < param->params_count; i++) {
Emil Gydesenbe424292023-02-25 16:47:19 +0100687 const struct bt_bap_broadcast_source_subgroup_param *subgroup_param;
Emil Gydesenc66f2102023-05-09 13:51:02 +0200688 struct bt_bap_broadcast_subgroup *subgroup;
Emil Gydesen40e39302022-09-05 11:29:06 +0200689
690 subgroup_param = &param->params[i];
691
692 subgroup = broadcast_source_new_subgroup(index);
693 if (subgroup == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100694 LOG_DBG("Could not allocate new broadcast subgroup");
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100695 broadcast_source_cleanup(source);
Emil Gydesen40e39302022-09-05 11:29:06 +0200696 return -ENOMEM;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100697 }
698
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200699 subgroup->codec_cfg = subgroup_param->codec_cfg;
Emil Gydesen40e39302022-09-05 11:29:06 +0200700 sys_slist_append(&source->subgroups, &subgroup->_node);
701
702 /* Check that we are not above the maximum BIS count */
703 if (subgroup_param->params_count + stream_count > BROADCAST_STREAM_CNT) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100704 LOG_DBG("Cannot create broadcaster with %zu streams", stream_count);
Emil Gydesen40e39302022-09-05 11:29:06 +0200705 broadcast_source_cleanup(source);
706
707 return -ENOMEM;
708 }
709
710 for (size_t j = 0U; j < subgroup_param->params_count; j++) {
Emil Gydesenbe424292023-02-25 16:47:19 +0100711 const struct bt_bap_broadcast_source_stream_param *stream_param;
Emil Gydesen77853f42023-02-27 15:32:52 +0100712 struct bt_bap_stream *stream;
Emil Gydesen40e39302022-09-05 11:29:06 +0200713
714 stream_param = &subgroup_param->params[j];
715 stream = stream_param->stream;
716
717 err = broadcast_source_setup_stream(index, stream,
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200718 subgroup_param->codec_cfg, qos, source);
Emil Gydesen40e39302022-09-05 11:29:06 +0200719 if (err != 0) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100720 LOG_DBG("Failed to setup streams[%zu]: %d", i, err);
Emil Gydesen40e39302022-09-05 11:29:06 +0200721 broadcast_source_cleanup(source);
722 return err;
723 }
724
725 /* Store the BIS specific codec configuration data in
726 * the broadcast source. It is stored in the broadcast
727 * source, instead of the stream object, as this is
728 * only relevant for the broadcast source, and not used
729 * for unicast or broadcast sink.
730 */
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200731 (void)memcpy(source->stream_data[stream_count].data, stream_param->data,
732 stream_param->data_len * sizeof(*stream_param->data));
733 source->stream_data[stream_count].data_len = stream_param->data_len;
Emil Gydesen40e39302022-09-05 11:29:06 +0200734
735 sys_slist_append(&subgroup->streams, &stream->_node);
736 stream_count++;
737 }
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100738 }
739
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100740 err = generate_broadcast_id(source);
741 if (err != 0) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100742 LOG_DBG("Could not generate broadcast id: %d", err);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100743 return err;
744 }
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100745
Emil Gydesen40e39302022-09-05 11:29:06 +0200746 /* Finalize state changes and store information */
Emil Gydesenc66f2102023-05-09 13:51:02 +0200747 broadcast_source_set_state(source, BT_BAP_EP_STATE_QOS_CONFIGURED);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100748 source->qos = qos;
Emil Gydesenba194972022-12-20 15:39:07 +0100749 source->packing = param->packing;
Emil Gydesen84c01bb2023-01-23 14:33:54 +0100750#if defined(CONFIG_BT_ISO_TEST_PARAMS)
751 source->irc = param->irc;
752 source->pto = param->pto;
753 source->iso_interval = param->iso_interval;
754#endif /* CONFIG_BT_ISO_TEST_PARAMS */
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100755
Emil Gydesen8d86fa02022-12-13 21:03:14 +0100756 source->encryption = param->encryption;
757 if (source->encryption) {
758 (void)memcpy(source->broadcast_code, param->broadcast_code,
759 sizeof(source->broadcast_code));
760 }
761
Théo Battrele458f5a2022-11-02 14:31:13 +0100762 LOG_DBG("Broadcasting with ID 0x%6X", source->broadcast_id);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100763
764 *out_source = source;
765
766 return 0;
767}
768
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200769int bt_bap_broadcast_source_reconfig(struct bt_bap_broadcast_source *source,
Emil Gydesend327b162023-08-17 13:15:12 +0200770 struct bt_bap_broadcast_source_param *param)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100771{
Emil Gydesen099c1382023-03-01 11:57:11 +0100772 struct bt_bap_broadcast_subgroup *subgroup;
Emil Gydesen33268632023-03-01 11:18:25 +0100773 enum bt_bap_ep_state broadcast_state;
Emil Gydesend327b162023-08-17 13:15:12 +0200774 struct bt_audio_codec_qos *qos;
775 size_t subgroup_cnt;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100776
777 CHECKIF(source == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100778 LOG_DBG("source is NULL");
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100779 return -EINVAL;
780 }
781
Emil Gydesend327b162023-08-17 13:15:12 +0200782 if (!valid_broadcast_source_param(param, source)) {
783 LOG_DBG("Invalid parameters");
Emil Gydesenfa95a7a2023-04-12 16:29:29 +0200784 return -EINVAL;
785 }
786
Emil Gydesen40e39302022-09-05 11:29:06 +0200787 broadcast_state = broadcast_source_get_state(source);
Emil Gydesen33268632023-03-01 11:18:25 +0100788 if (broadcast_source_get_state(source) != BT_BAP_EP_STATE_QOS_CONFIGURED) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100789 LOG_DBG("Broadcast source invalid state: %u", broadcast_state);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100790 return -EBADMSG;
791 }
792
Emil Gydesend327b162023-08-17 13:15:12 +0200793 /* Verify that the parameter counts do not exceed existing number of subgroups and streams*/
794 subgroup_cnt = 0U;
Emil Gydesen40e39302022-09-05 11:29:06 +0200795 SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
Emil Gydesend327b162023-08-17 13:15:12 +0200796 const struct bt_bap_broadcast_source_subgroup_param *subgroup_param =
797 &param->params[subgroup_cnt];
798 const size_t subgroup_stream_param_cnt = subgroup_param->params_count;
799 struct bt_bap_stream *stream;
Emil Gydesene962fda2023-10-04 17:29:33 +0200800 size_t subgroup_stream_cnt = 0U;
Emil Gydesend327b162023-08-17 13:15:12 +0200801
Emil Gydesen40e39302022-09-05 11:29:06 +0200802 SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
Emil Gydesene962fda2023-10-04 17:29:33 +0200803 subgroup_stream_cnt++;
Emil Gydesend327b162023-08-17 13:15:12 +0200804 }
805
806 /* Verify that the param stream is in the subgroup */
807 for (size_t i = 0U; i < subgroup_param->params_count; i++) {
808 struct bt_bap_stream *subgroup_stream;
809 struct bt_bap_stream *param_stream;
Emil Gydesenf4cbf402024-01-03 09:58:07 +0100810 bool stream_in_subgroup = false;
Emil Gydesend327b162023-08-17 13:15:12 +0200811
812 param_stream = subgroup_param->params[i].stream;
813
814 SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, subgroup_stream, _node) {
815 if (subgroup_stream == param_stream) {
816 stream_in_subgroup = true;
817 break;
818 }
819 }
820
821 if (!stream_in_subgroup) {
822 LOG_DBG("Invalid param->params[%zu]->param[%zu].stream "
823 "not in subgroup",
824 subgroup_cnt, i);
825 return -EINVAL;
826 }
827 }
828
Emil Gydesene962fda2023-10-04 17:29:33 +0200829 if (subgroup_stream_cnt < subgroup_stream_param_cnt) {
Emil Gydesend327b162023-08-17 13:15:12 +0200830 LOG_DBG("Invalid param->params[%zu]->params_count: %zu "
831 "(only %zu streams in subgroup)",
Emil Gydesene962fda2023-10-04 17:29:33 +0200832 subgroup_cnt, subgroup_stream_param_cnt, subgroup_stream_cnt);
Emil Gydesend327b162023-08-17 13:15:12 +0200833 return -EINVAL;
834 }
835
836 subgroup_cnt++;
837 }
838
839 if (subgroup_cnt < param->params_count) {
840 LOG_DBG("Invalid param->params_count: %zu (only %zu subgroups in source)",
841 param->params_count, subgroup_cnt);
842 return -EINVAL;
843 }
844
845 qos = param->qos;
846
847 /* We update up to the first param->params_count subgroups */
848 for (size_t i = 0U; i < param->params_count; i++) {
849 const struct bt_bap_broadcast_source_subgroup_param *subgroup_param;
850 struct bt_audio_codec_cfg *codec_cfg;
Emil Gydesende67ec92023-10-05 14:14:04 +0200851 struct bt_bap_stream *stream;
Emil Gydesend327b162023-08-17 13:15:12 +0200852
853 if (i == 0) {
854 subgroup =
855 SYS_SLIST_PEEK_HEAD_CONTAINER(&source->subgroups, subgroup, _node);
856 } else {
857 subgroup = SYS_SLIST_PEEK_NEXT_CONTAINER(subgroup, _node);
858 }
859
860 subgroup_param = &param->params[i];
861 codec_cfg = subgroup_param->codec_cfg;
862 subgroup->codec_cfg = codec_cfg;
863
864 for (size_t j = 0U; j < subgroup_param->params_count; j++) {
865 const struct bt_bap_broadcast_source_stream_param *stream_param;
866 struct bt_audio_broadcast_stream_data *stream_data;
867 struct bt_bap_stream *subgroup_stream;
Emil Gydesend327b162023-08-17 13:15:12 +0200868 size_t stream_idx;
869
870 stream_param = &subgroup_param->params[j];
871 stream = stream_param->stream;
872
873 stream_idx = 0U;
874 SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, subgroup_stream, _node) {
875 if (subgroup_stream == stream) {
Emil Gydesend327b162023-08-17 13:15:12 +0200876 break;
877 }
878
879 stream_idx++;
880 }
Emil Gydesene5890fc2023-01-24 14:41:17 +0100881
Emil Gydesend327b162023-08-17 13:15:12 +0200882 /* Store the BIS specific codec configuration data in the broadcast source.
883 * It is stored in the broadcast* source, instead of the stream object,
884 * as this is only relevant for the broadcast source, and not used
885 * for unicast or broadcast sink.
886 */
887 stream_data = &source->stream_data[stream_idx];
888 (void)memcpy(stream_data->data, stream_param->data, stream_param->data_len);
889 stream_data->data_len = stream_param->data_len;
Emil Gydesen40e39302022-09-05 11:29:06 +0200890 }
Emil Gydesende67ec92023-10-05 14:14:04 +0200891
892 /* Apply the codec_cfg to all streams in the subgroup, and not just the ones in the
893 * params
894 */
895 SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
896 struct bt_iso_chan_io_qos *iso_qos;
897
898 iso_qos = stream->ep->iso->chan.qos->tx;
899 bt_bap_stream_attach(NULL, stream, stream->ep, codec_cfg);
Emil Gydesenaef39f62024-01-19 13:13:08 +0100900 bt_bap_iso_configure_data_path(stream->ep, codec_cfg);
Emil Gydesende67ec92023-10-05 14:14:04 +0200901 }
902 }
903
904 /* Finally we apply the new qos and to all streams */
905 SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
906 struct bt_bap_stream *stream;
907
908 SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
909 struct bt_iso_chan_io_qos *iso_qos;
910
911 iso_qos = stream->ep->iso->chan.qos->tx;
912 bt_audio_codec_qos_to_iso_qos(iso_qos, qos);
913 stream->qos = qos;
914 }
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100915 }
916
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100917 source->qos = qos;
918
919 return 0;
920}
921
Emil Gydesenbe424292023-02-25 16:47:19 +0100922int bt_bap_broadcast_source_update_metadata(struct bt_bap_broadcast_source *source,
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200923 const uint8_t meta[], size_t meta_len)
Emil Gydesen1424df42022-06-28 20:09:13 +0200924{
Emil Gydesen099c1382023-03-01 11:57:11 +0100925 struct bt_bap_broadcast_subgroup *subgroup;
Emil Gydesen33268632023-03-01 11:18:25 +0100926 enum bt_bap_ep_state broadcast_state;
Emil Gydesen1424df42022-06-28 20:09:13 +0200927
928 CHECKIF(source == NULL) {
929 LOG_DBG("source is NULL");
930
931 return -EINVAL;
932 }
933
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200934 CHECKIF((meta == NULL && meta_len != 0) || (meta != NULL && meta_len == 0)) {
935 LOG_DBG("Invalid metadata combination: %p %zu", meta, meta_len);
Emil Gydesen1424df42022-06-28 20:09:13 +0200936
937 return -EINVAL;
938 }
939
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200940 CHECKIF(meta_len > CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE) {
941 LOG_DBG("Invalid meta_len: %zu (max %d)", meta_len,
942 CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE);
Emil Gydesen1424df42022-06-28 20:09:13 +0200943
944 return -EINVAL;
945 }
946
Emil Gydesen1424df42022-06-28 20:09:13 +0200947 broadcast_state = broadcast_source_get_state(source);
Emil Gydesen33268632023-03-01 11:18:25 +0100948 if (broadcast_source_get_state(source) != BT_BAP_EP_STATE_STREAMING) {
Emil Gydesen1424df42022-06-28 20:09:13 +0200949 LOG_DBG("Broadcast source invalid state: %u", broadcast_state);
950
951 return -EBADMSG;
952 }
953
954 /* TODO: We should probably find a way to update the metadata
955 * for each subgroup individually
956 */
957 SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
Emil Gydesen6ccd1122023-05-22 15:08:57 +0200958 memset(subgroup->codec_cfg->meta, 0, sizeof(subgroup->codec_cfg->meta));
959 memcpy(subgroup->codec_cfg->meta, meta, meta_len);
Magdalena Kasenberge05d9642023-12-20 16:47:34 +0100960 subgroup->codec_cfg->meta_len = meta_len;
Emil Gydesen1424df42022-06-28 20:09:13 +0200961 }
962
963 return 0;
964}
965
Emil Gydesenbe424292023-02-25 16:47:19 +0100966int bt_bap_broadcast_source_start(struct bt_bap_broadcast_source *source, struct bt_le_ext_adv *adv)
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100967{
Emil Gydesen40e39302022-09-05 11:29:06 +0200968 struct bt_iso_chan *bis[BROADCAST_STREAM_CNT];
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100969 struct bt_iso_big_create_param param = { 0 };
Emil Gydesen099c1382023-03-01 11:57:11 +0100970 struct bt_bap_broadcast_subgroup *subgroup;
Emil Gydesen33268632023-03-01 11:18:25 +0100971 enum bt_bap_ep_state broadcast_state;
Emil Gydesen77853f42023-02-27 15:32:52 +0100972 struct bt_bap_stream *stream;
Emil Gydesen40e39302022-09-05 11:29:06 +0200973 size_t bis_count;
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100974 int err;
975
976 CHECKIF(source == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100977 LOG_DBG("source is NULL");
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100978 return -EINVAL;
979 }
980
Emil Gydesenafd41612023-05-09 15:13:45 +0200981 CHECKIF(adv == NULL) {
982 LOG_DBG("adv is NULL");
983 return -EINVAL;
984 }
985
Emil Gydesen40e39302022-09-05 11:29:06 +0200986 broadcast_state = broadcast_source_get_state(source);
Emil Gydesen33268632023-03-01 11:18:25 +0100987 if (broadcast_source_get_state(source) != BT_BAP_EP_STATE_QOS_CONFIGURED) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100988 LOG_DBG("Broadcast source invalid state: %u", broadcast_state);
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100989 return -EBADMSG;
990 }
991
Emil Gydesen40e39302022-09-05 11:29:06 +0200992 bis_count = 0;
993 SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
994 SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
Emil Gydesen77853f42023-02-27 15:32:52 +0100995 bis[bis_count++] = bt_bap_stream_iso_chan_get(stream);
Emil Gydesen40e39302022-09-05 11:29:06 +0200996 }
997 }
998
Emil Gydesenab87e0a2022-01-10 15:06:08 +0100999 /* Create BIG */
Emil Gydesen40e39302022-09-05 11:29:06 +02001000 param.num_bis = bis_count;
1001 param.bis_channels = bis;
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001002 param.framing = source->qos->framing;
Emil Gydesenba194972022-12-20 15:39:07 +01001003 param.packing = source->packing;
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001004 param.interval = source->qos->interval;
1005 param.latency = source->qos->latency;
Emil Gydesen8d86fa02022-12-13 21:03:14 +01001006 param.encryption = source->encryption;
1007 if (param.encryption) {
1008 (void)memcpy(param.bcode, source->broadcast_code,
1009 sizeof(param.bcode));
1010 }
Emil Gydesen84c01bb2023-01-23 14:33:54 +01001011#if defined(CONFIG_BT_ISO_TEST_PARAMS)
1012 param.irc = source->irc;
1013 param.pto = source->pto;
1014 param.iso_interval = source->iso_interval;
1015#endif /* CONFIG_BT_ISO_TEST_PARAMS */
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001016
Emil Gydesen68a4d0f2023-08-31 15:30:31 +02001017 /* Set the enabling state early in case that the BIS is connected before we can manage to
1018 * set it afterwards
1019 */
1020 broadcast_source_set_state(source, BT_BAP_EP_STATE_ENABLING);
1021
Emil Gydesen7ab1caf2022-09-02 15:43:30 +02001022 err = bt_iso_big_create(adv, &param, &source->big);
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001023 if (err != 0) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001024 LOG_DBG("Failed to create BIG: %d", err);
Emil Gydesen68a4d0f2023-08-31 15:30:31 +02001025 broadcast_source_set_state(source, BT_BAP_EP_STATE_QOS_CONFIGURED);
1026
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001027 return err;
1028 }
1029
1030 return 0;
1031}
1032
Emil Gydesenbe424292023-02-25 16:47:19 +01001033int bt_bap_broadcast_source_stop(struct bt_bap_broadcast_source *source)
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001034{
Emil Gydesen33268632023-03-01 11:18:25 +01001035 enum bt_bap_ep_state broadcast_state;
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001036 int err;
1037
1038 CHECKIF(source == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001039 LOG_DBG("source is NULL");
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001040 return -EINVAL;
1041 }
1042
Emil Gydesen40e39302022-09-05 11:29:06 +02001043 broadcast_state = broadcast_source_get_state(source);
Emil Gydesen33268632023-03-01 11:18:25 +01001044 if (broadcast_state != BT_BAP_EP_STATE_STREAMING &&
1045 broadcast_state != BT_BAP_EP_STATE_ENABLING) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001046 LOG_DBG("Broadcast source invalid state: %u", broadcast_state);
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001047 return -EBADMSG;
1048 }
1049
1050 if (source->big == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001051 LOG_DBG("Source is not started");
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001052 return -EALREADY;
1053 }
1054
1055 err = bt_iso_big_terminate(source->big);
1056 if (err) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001057 LOG_DBG("Failed to terminate BIG (err %d)", err);
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001058 return err;
1059 }
1060
1061 source->big = NULL;
1062
1063 return 0;
1064}
1065
Emil Gydesenbe424292023-02-25 16:47:19 +01001066int bt_bap_broadcast_source_delete(struct bt_bap_broadcast_source *source)
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001067{
Emil Gydesen33268632023-03-01 11:18:25 +01001068 enum bt_bap_ep_state broadcast_state;
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001069
1070 CHECKIF(source == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001071 LOG_DBG("source is NULL");
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001072 return -EINVAL;
1073 }
1074
Emil Gydesen40e39302022-09-05 11:29:06 +02001075 broadcast_state = broadcast_source_get_state(source);
Emil Gydesen33268632023-03-01 11:18:25 +01001076 if (broadcast_state != BT_BAP_EP_STATE_QOS_CONFIGURED) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001077 LOG_DBG("Broadcast source invalid state: %u", broadcast_state);
Emil Gydesen468bd4d2022-03-11 14:58:51 +01001078 return -EBADMSG;
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001079 }
1080
Emil Gydesenc66f2102023-05-09 13:51:02 +02001081 broadcast_source_set_state(source, BT_BAP_EP_STATE_IDLE);
1082
Emil Gydesenab87e0a2022-01-10 15:06:08 +01001083 /* Reset the broadcast source */
1084 broadcast_source_cleanup(source);
1085
1086 return 0;
1087}
Emil Gydesen7ab1caf2022-09-02 15:43:30 +02001088
Emil Gydesen9d62bef2023-09-08 11:29:44 +02001089int bt_bap_broadcast_source_get_id(struct bt_bap_broadcast_source *source,
Emil Gydesenbe424292023-02-25 16:47:19 +01001090 uint32_t *const broadcast_id)
Emil Gydesen7ab1caf2022-09-02 15:43:30 +02001091{
Emil Gydesen9d62bef2023-09-08 11:29:44 +02001092 enum bt_bap_ep_state broadcast_state;
1093
Emil Gydesen7ab1caf2022-09-02 15:43:30 +02001094 CHECKIF(source == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001095 LOG_DBG("source is NULL");
Emil Gydesen7ab1caf2022-09-02 15:43:30 +02001096 return -EINVAL;
1097 }
1098
1099 CHECKIF(broadcast_id == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001100 LOG_DBG("broadcast_id is NULL");
Emil Gydesen7ab1caf2022-09-02 15:43:30 +02001101 return -EINVAL;
1102 }
1103
Emil Gydesen9d62bef2023-09-08 11:29:44 +02001104 broadcast_state = broadcast_source_get_state(source);
1105 if (broadcast_state == BT_BAP_EP_STATE_IDLE) {
1106 LOG_DBG("Broadcast source invalid state: %u", broadcast_state);
1107 return -EBADMSG;
1108 }
1109
Emil Gydesen7ab1caf2022-09-02 15:43:30 +02001110 *broadcast_id = source->broadcast_id;
1111
1112 return 0;
1113}
1114
Emil Gydesenbe424292023-02-25 16:47:19 +01001115int bt_bap_broadcast_source_get_base(struct bt_bap_broadcast_source *source,
1116 struct net_buf_simple *base_buf)
Emil Gydesen7ab1caf2022-09-02 15:43:30 +02001117{
Emil Gydesen9d62bef2023-09-08 11:29:44 +02001118 enum bt_bap_ep_state broadcast_state;
1119
Emil Gydesenfa95a7a2023-04-12 16:29:29 +02001120 CHECKIF(source == NULL) {
1121 LOG_DBG("source is NULL");
1122 return -EINVAL;
1123 }
1124
1125 CHECKIF(base_buf == NULL) {
1126 LOG_DBG("base_buf is NULL");
1127 return -EINVAL;
1128 }
1129
Emil Gydesen9d62bef2023-09-08 11:29:44 +02001130 broadcast_state = broadcast_source_get_state(source);
1131 if (broadcast_state == BT_BAP_EP_STATE_IDLE) {
1132 LOG_DBG("Broadcast source invalid state: %u", broadcast_state);
1133 return -EBADMSG;
1134 }
1135
Emil Gydesen40e39302022-09-05 11:29:06 +02001136 if (!encode_base(source, base_buf)) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001137 LOG_DBG("base_buf %p with size %u not large enough", base_buf, base_buf->size);
Emil Gydesen7ab1caf2022-09-02 15:43:30 +02001138
1139 return -EMSGSIZE;
1140 }
1141
1142 return 0;
1143}