1 /* bap_base.c - BAP BASE handling */
2
3 /*
4 * Copyright (c) 2023 Nordic Semiconductor ASA
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <string.h>
14
15 #include <zephyr/autoconf.h>
16 #include <zephyr/bluetooth/audio/audio.h>
17 #include <zephyr/bluetooth/audio/bap.h>
18 #include <zephyr/bluetooth/bluetooth.h>
19 #include <zephyr/bluetooth/gap.h>
20 #include <zephyr/bluetooth/iso.h>
21 #include <zephyr/bluetooth/uuid.h>
22 #include <zephyr/logging/log.h>
23 #include <zephyr/net_buf.h>
24 #include <zephyr/sys/check.h>
25 #include <zephyr/sys/util.h>
26 #include <zephyr/sys/util_macro.h>
27
28 LOG_MODULE_REGISTER(bt_bap_base, CONFIG_BT_BAP_BASE_LOG_LEVEL);
29
30 /* The BASE and the following defines are defined by BAP v1.0.1, section 3.7.2.2 Basic Audio
31 * Announcements
32 */
33 #define BASE_MAX_SIZE (UINT8_MAX - 1 /* type */ - BT_UUID_SIZE_16)
34 #define BASE_CODEC_ID_SIZE (1 /* id */ + 2 /* cid */ + 2 /* vid */)
35 #define BASE_PD_SIZE 3
36 #define BASE_SUBGROUP_COUNT_SIZE 1
37 #define BASE_NUM_BIS_SIZE 1
38 #define BASE_CC_LEN_SIZE 1
39 #define BASE_META_LEN_SIZE 1
40 #define BASE_BIS_INDEX_SIZE 1
41 #define BASE_BIS_CC_LEN_SIZE 1
42 #define BASE_SUBGROUP_MAX_SIZE (BASE_MAX_SIZE - BASE_PD_SIZE - BASE_SUBGROUP_COUNT_SIZE)
43 #define BASE_SUBGROUP_MIN_SIZE \
44 (BASE_NUM_BIS_SIZE + BASE_CODEC_ID_SIZE + BASE_CC_LEN_SIZE + BASE_META_LEN_SIZE + \
45 BASE_BIS_INDEX_SIZE + BASE_BIS_CC_LEN_SIZE)
46 #define BASE_MIN_SIZE \
47 (BT_UUID_SIZE_16 + BASE_PD_SIZE + BASE_SUBGROUP_COUNT_SIZE + BASE_SUBGROUP_MIN_SIZE)
48 #define BASE_SUBGROUP_MAX_COUNT \
49 ((BASE_MAX_SIZE - BASE_PD_SIZE - BASE_SUBGROUP_COUNT_SIZE) / BASE_SUBGROUP_MIN_SIZE)
50
base_pull_pd(struct net_buf_simple * net_buf)51 static uint32_t base_pull_pd(struct net_buf_simple *net_buf)
52 {
53 return net_buf_simple_pull_le24(net_buf);
54 }
55
base_pull_bis_count(struct net_buf_simple * net_buf)56 static uint8_t base_pull_bis_count(struct net_buf_simple *net_buf)
57 {
58 return net_buf_simple_pull_u8(net_buf);
59 }
60
base_pull_codec_id(struct net_buf_simple * net_buf,struct bt_bap_base_codec_id * codec_id)61 static void base_pull_codec_id(struct net_buf_simple *net_buf,
62 struct bt_bap_base_codec_id *codec_id)
63 {
64 struct bt_bap_base_codec_id codec;
65
66 codec.id = net_buf_simple_pull_u8(net_buf); /* coding format */
67 codec.cid = net_buf_simple_pull_le16(net_buf); /* company id */
68 codec.vid = net_buf_simple_pull_le16(net_buf); /* VS codec id */
69
70 if (codec_id != NULL) {
71 *codec_id = codec;
72 }
73 }
74
base_pull_ltv(struct net_buf_simple * net_buf,uint8_t ** data)75 static uint8_t base_pull_ltv(struct net_buf_simple *net_buf, uint8_t **data)
76 {
77 const uint8_t len = net_buf_simple_pull_u8(net_buf);
78
79 if (data == NULL) {
80 net_buf_simple_pull_mem(net_buf, len);
81 } else {
82 *data = net_buf_simple_pull_mem(net_buf, len);
83 }
84
85 return len;
86 }
87
check_pull_ltv(struct net_buf_simple * net_buf)88 static bool check_pull_ltv(struct net_buf_simple *net_buf)
89 {
90 uint8_t ltv_len;
91
92 if (net_buf->len < sizeof(ltv_len)) {
93 return false;
94 }
95
96 ltv_len = net_buf_simple_pull_u8(net_buf);
97 if (net_buf->len < ltv_len) {
98 return false;
99 }
100 net_buf_simple_pull_mem(net_buf, ltv_len);
101
102 return true;
103 }
104
bt_bap_base_get_base_from_ad(const struct bt_data * ad)105 const struct bt_bap_base *bt_bap_base_get_base_from_ad(const struct bt_data *ad)
106 {
107 struct bt_uuid_16 broadcast_uuid;
108 const struct bt_bap_base *base;
109 struct net_buf_simple net_buf;
110 uint8_t subgroup_count;
111 void *uuid;
112
113 CHECKIF(ad == NULL) {
114 LOG_DBG("data is NULL");
115
116 return NULL;
117 }
118
119 if (ad->type != BT_DATA_SVC_DATA16) {
120 LOG_DBG("Invalid type: %u", ad->type);
121
122 return NULL;
123 }
124
125 if (ad->data_len < BASE_MIN_SIZE) {
126 LOG_DBG("Invalid len: %u", ad->data_len);
127
128 return NULL;
129 }
130
131 net_buf_simple_init_with_data(&net_buf, (void *)ad->data, ad->data_len);
132
133 uuid = net_buf_simple_pull_mem(&net_buf, BT_UUID_SIZE_16);
134 if (!bt_uuid_create(&broadcast_uuid.uuid, uuid, BT_UUID_SIZE_16)) {
135 LOG_ERR("bt_uuid_create failed");
136
137 return NULL;
138 }
139
140 if (bt_uuid_cmp(&broadcast_uuid.uuid, BT_UUID_BASIC_AUDIO) != 0) {
141 LOG_DBG("Invalid UUID");
142
143 return NULL;
144 }
145
146 /* Store the start of the BASE */
147 base = (const struct bt_bap_base *)net_buf.data;
148
149 /* Pull all data to verify that the result BASE is valid */
150 base_pull_pd(&net_buf);
151 subgroup_count = net_buf_simple_pull_u8(&net_buf);
152 if (subgroup_count == 0 || subgroup_count > BASE_SUBGROUP_MAX_COUNT) {
153 LOG_DBG("Invalid subgroup count: %u", subgroup_count);
154
155 return NULL;
156 }
157
158 for (uint8_t i = 0U; i < subgroup_count; i++) {
159 uint8_t bis_count;
160
161 if (net_buf.len < sizeof(bis_count)) {
162 LOG_DBG("Invalid BASE length: %u", ad->data_len);
163
164 return NULL;
165 }
166
167 bis_count = base_pull_bis_count(&net_buf);
168 if (bis_count == 0 || bis_count > BT_ISO_MAX_GROUP_ISO_COUNT) {
169 LOG_DBG("Subgroup[%u]: Invalid BIS count: %u", i, bis_count);
170
171 return NULL;
172 }
173
174 if (net_buf.len < BASE_CODEC_ID_SIZE) {
175 LOG_DBG("Invalid BASE length: %u", ad->data_len);
176
177 return NULL;
178 }
179
180 base_pull_codec_id(&net_buf, NULL);
181
182 /* Pull CC */
183 if (!check_pull_ltv(&net_buf)) {
184 LOG_DBG("Invalid BASE length: %u", ad->data_len);
185
186 return NULL;
187 }
188
189 /* Pull meta */
190 if (!check_pull_ltv(&net_buf)) {
191 LOG_DBG("Invalid BASE length: %u", ad->data_len);
192
193 return NULL;
194 }
195
196 for (uint8_t j = 0U; j < bis_count; j++) {
197 uint8_t bis_index;
198
199 if (net_buf.len < sizeof(bis_index)) {
200 LOG_DBG("Invalid BASE length: %u", ad->data_len);
201
202 return NULL;
203 }
204
205 bis_index = net_buf_simple_pull_u8(&net_buf);
206 if (bis_index == 0 || bis_index > BT_ISO_BIS_INDEX_MAX) {
207 LOG_DBG("Subgroup[%u]: Invalid BIS index: %u", i, bis_index);
208
209 return NULL;
210 }
211
212 /* Pull BIS CC data */
213 if (!check_pull_ltv(&net_buf)) {
214 LOG_DBG("Invalid BASE length: %u", ad->data_len);
215
216 return NULL;
217 }
218 }
219 }
220
221 return base;
222 }
223
bt_bap_base_get_size(const struct bt_bap_base * base)224 int bt_bap_base_get_size(const struct bt_bap_base *base)
225 {
226 struct net_buf_simple net_buf;
227 uint8_t subgroup_count;
228 size_t size = 0;
229
230 CHECKIF(base == NULL) {
231 LOG_DBG("base is NULL");
232
233 return -EINVAL;
234 }
235
236 net_buf_simple_init_with_data(&net_buf, (void *)base, BASE_MAX_SIZE);
237 base_pull_pd(&net_buf);
238 size += BASE_PD_SIZE;
239 subgroup_count = net_buf_simple_pull_u8(&net_buf);
240 size += BASE_SUBGROUP_COUNT_SIZE;
241
242 /* Parse subgroup data */
243 for (uint8_t i = 0U; i < subgroup_count; i++) {
244 uint8_t bis_count;
245 uint8_t len;
246
247 bis_count = base_pull_bis_count(&net_buf);
248 size += BASE_NUM_BIS_SIZE;
249
250 base_pull_codec_id(&net_buf, NULL);
251 size += BASE_CODEC_ID_SIZE;
252
253 /* Codec config */
254 len = base_pull_ltv(&net_buf, NULL);
255 size += len + BASE_CC_LEN_SIZE;
256
257 /* meta */
258 len = base_pull_ltv(&net_buf, NULL);
259 size += len + BASE_META_LEN_SIZE;
260
261 /* Parse BIS data */
262 for (uint8_t j = 0U; j < bis_count; j++) {
263 /* BIS index */
264 net_buf_simple_pull_u8(&net_buf);
265 size += BASE_BIS_INDEX_SIZE;
266
267 /* Codec config */
268 len = base_pull_ltv(&net_buf, NULL);
269 size += len + BASE_BIS_CC_LEN_SIZE;
270 }
271 }
272
273 return (int)size;
274 }
275
bt_bap_base_get_pres_delay(const struct bt_bap_base * base)276 int bt_bap_base_get_pres_delay(const struct bt_bap_base *base)
277 {
278 struct net_buf_simple net_buf;
279 uint32_t pd;
280
281 CHECKIF(base == NULL) {
282 LOG_DBG("base is NULL");
283
284 return -EINVAL;
285 }
286
287 net_buf_simple_init_with_data(&net_buf, (void *)base, sizeof(pd));
288 pd = base_pull_pd(&net_buf);
289
290 return (int)pd; /* PD is 24-bit so it fits in an int */
291 }
292
bt_bap_base_get_subgroup_count(const struct bt_bap_base * base)293 int bt_bap_base_get_subgroup_count(const struct bt_bap_base *base)
294 {
295 struct net_buf_simple net_buf;
296 uint8_t subgroup_count;
297
298 CHECKIF(base == NULL) {
299 LOG_DBG("base is NULL");
300
301 return -EINVAL;
302 }
303
304 net_buf_simple_init_with_data(&net_buf, (void *)base, BASE_MAX_SIZE);
305 base_pull_pd(&net_buf);
306 subgroup_count = net_buf_simple_pull_u8(&net_buf);
307
308 return (int)subgroup_count; /* subgroup_count is 8-bit so it fits in an int */
309 }
310
bt_bap_base_foreach_subgroup(const struct bt_bap_base * base,bool (* func)(const struct bt_bap_base_subgroup * data,void * user_data),void * user_data)311 int bt_bap_base_foreach_subgroup(const struct bt_bap_base *base,
312 bool (*func)(const struct bt_bap_base_subgroup *data,
313 void *user_data),
314 void *user_data)
315 {
316 struct bt_bap_base_subgroup *subgroup;
317 struct net_buf_simple net_buf;
318 uint8_t subgroup_count;
319
320 CHECKIF(base == NULL) {
321 LOG_DBG("base is NULL");
322
323 return -EINVAL;
324 }
325
326 CHECKIF(func == NULL) {
327 LOG_DBG("func is NULL");
328
329 return -EINVAL;
330 }
331
332 net_buf_simple_init_with_data(&net_buf, (void *)base, BASE_MAX_SIZE);
333 base_pull_pd(&net_buf);
334 subgroup_count = net_buf_simple_pull_u8(&net_buf);
335
336 for (uint8_t i = 0U; i < subgroup_count; i++) {
337 subgroup = (struct bt_bap_base_subgroup *)net_buf.data;
338 if (!func(subgroup, user_data)) {
339 LOG_DBG("user stopped parsing");
340
341 return -ECANCELED;
342 }
343
344 /* Parse subgroup data to get next subgroup pointer */
345 if (subgroup_count > 1) { /* Only parse data if it isn't the last one */
346 uint8_t bis_count;
347
348 bis_count = base_pull_bis_count(&net_buf);
349 base_pull_codec_id(&net_buf, NULL);
350
351 /* Codec config */
352 base_pull_ltv(&net_buf, NULL);
353
354 /* meta */
355 base_pull_ltv(&net_buf, NULL);
356
357 for (uint8_t j = 0U; j < bis_count; j++) {
358 net_buf_simple_pull_u8(&net_buf); /* index */
359
360 /* Codec config */
361 base_pull_ltv(&net_buf, NULL);
362 }
363 }
364 }
365
366 return 0;
367 }
368
bt_bap_base_get_subgroup_codec_id(const struct bt_bap_base_subgroup * subgroup,struct bt_bap_base_codec_id * codec_id)369 int bt_bap_base_get_subgroup_codec_id(const struct bt_bap_base_subgroup *subgroup,
370 struct bt_bap_base_codec_id *codec_id)
371 {
372 struct net_buf_simple net_buf;
373
374 CHECKIF(subgroup == NULL) {
375 LOG_DBG("subgroup is NULL");
376
377 return -EINVAL;
378 }
379
380 CHECKIF(codec_id == NULL) {
381 LOG_DBG("codec_id is NULL");
382
383 return -EINVAL;
384 }
385
386 net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
387 base_pull_bis_count(&net_buf);
388 base_pull_codec_id(&net_buf, codec_id);
389
390 return 0;
391 }
392
bt_bap_base_get_subgroup_codec_data(const struct bt_bap_base_subgroup * subgroup,uint8_t ** data)393 int bt_bap_base_get_subgroup_codec_data(const struct bt_bap_base_subgroup *subgroup, uint8_t **data)
394 {
395 struct net_buf_simple net_buf;
396
397 CHECKIF(subgroup == NULL) {
398 LOG_DBG("subgroup is NULL");
399
400 return -EINVAL;
401 }
402
403 CHECKIF(data == NULL) {
404 LOG_DBG("data is NULL");
405
406 return -EINVAL;
407 }
408
409 net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
410 base_pull_bis_count(&net_buf);
411 base_pull_codec_id(&net_buf, NULL);
412
413 /* Codec config */
414 return base_pull_ltv(&net_buf, data);
415 }
416
bt_bap_base_get_subgroup_codec_meta(const struct bt_bap_base_subgroup * subgroup,uint8_t ** meta)417 int bt_bap_base_get_subgroup_codec_meta(const struct bt_bap_base_subgroup *subgroup, uint8_t **meta)
418 {
419 struct net_buf_simple net_buf;
420
421 CHECKIF(subgroup == NULL) {
422 LOG_DBG("subgroup is NULL");
423
424 return -EINVAL;
425 }
426
427 CHECKIF(meta == NULL) {
428 LOG_DBG("meta is NULL");
429
430 return -EINVAL;
431 }
432
433 net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
434 base_pull_bis_count(&net_buf);
435 base_pull_codec_id(&net_buf, NULL);
436
437 /* Codec config */
438 base_pull_ltv(&net_buf, NULL);
439
440 /* meta */
441 return base_pull_ltv(&net_buf, meta);
442 }
443
bt_bap_base_subgroup_codec_to_codec_cfg(const struct bt_bap_base_subgroup * subgroup,struct bt_audio_codec_cfg * codec_cfg)444 int bt_bap_base_subgroup_codec_to_codec_cfg(const struct bt_bap_base_subgroup *subgroup,
445 struct bt_audio_codec_cfg *codec_cfg)
446 {
447 struct bt_bap_base_codec_id codec_id;
448 struct net_buf_simple net_buf;
449 uint8_t *ltv_data;
450 uint8_t ltv_len;
451
452 CHECKIF(subgroup == NULL) {
453 LOG_DBG("subgroup is NULL");
454
455 return -EINVAL;
456 }
457
458 CHECKIF(codec_cfg == NULL) {
459 LOG_DBG("codec_cfg is NULL");
460
461 return -EINVAL;
462 }
463
464 net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
465 base_pull_bis_count(&net_buf);
466 base_pull_codec_id(&net_buf, &codec_id);
467
468 codec_cfg->id = codec_id.id;
469 codec_cfg->cid = codec_id.cid;
470 codec_cfg->vid = codec_id.vid;
471
472 /* Codec config */
473 ltv_len = base_pull_ltv(&net_buf, <v_data);
474
475 if (ltv_len > ARRAY_SIZE(codec_cfg->data)) {
476 LOG_DBG("Cannot fit %u octets of codec data (max %zu)", ltv_len,
477 ARRAY_SIZE(codec_cfg->data));
478
479 return -ENOMEM;
480 }
481
482 codec_cfg->data_len = ltv_len;
483 memcpy(codec_cfg->data, ltv_data, ltv_len);
484
485 /* Meta */
486 ltv_len = base_pull_ltv(&net_buf, <v_data);
487
488 if (ltv_len > ARRAY_SIZE(codec_cfg->meta)) {
489 LOG_DBG("Cannot fit %u octets of codec meta (max %zu)", ltv_len,
490 ARRAY_SIZE(codec_cfg->meta));
491
492 return -ENOMEM;
493 }
494
495 codec_cfg->meta_len = ltv_len;
496 memcpy(codec_cfg->meta, ltv_data, ltv_len);
497
498 return 0;
499 }
bt_bap_base_get_subgroup_bis_count(const struct bt_bap_base_subgroup * subgroup)500 int bt_bap_base_get_subgroup_bis_count(const struct bt_bap_base_subgroup *subgroup)
501 {
502 struct net_buf_simple net_buf;
503
504 CHECKIF(subgroup == NULL) {
505 LOG_DBG("subgroup is NULL");
506
507 return -EINVAL;
508 }
509
510 net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
511
512 return base_pull_bis_count(&net_buf);
513 }
514
bt_bap_base_subgroup_foreach_bis(const struct bt_bap_base_subgroup * subgroup,bool (* func)(const struct bt_bap_base_subgroup_bis * subgroup,void * user_data),void * user_data)515 int bt_bap_base_subgroup_foreach_bis(const struct bt_bap_base_subgroup *subgroup,
516 bool (*func)(const struct bt_bap_base_subgroup_bis *subgroup,
517 void *user_data),
518 void *user_data)
519 {
520 struct net_buf_simple net_buf;
521 uint8_t bis_count;
522
523 CHECKIF(subgroup == NULL) {
524 LOG_DBG("subgroup is NULL");
525
526 return -EINVAL;
527 }
528
529 CHECKIF(func == NULL) {
530 LOG_DBG("func is NULL");
531
532 return -EINVAL;
533 }
534
535 net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
536
537 bis_count = base_pull_bis_count(&net_buf);
538 base_pull_codec_id(&net_buf, NULL);
539
540 /* Codec config */
541 base_pull_ltv(&net_buf, NULL);
542
543 /* meta */
544 base_pull_ltv(&net_buf, NULL);
545
546 for (uint8_t i = 0U; i < bis_count; i++) {
547 struct bt_bap_base_subgroup_bis bis;
548
549 bis.index = net_buf_simple_pull_u8(&net_buf); /* index */
550
551 /* Codec config */
552 bis.data_len = base_pull_ltv(&net_buf, &bis.data);
553
554 if (!func(&bis, user_data)) {
555 LOG_DBG("user stopped parsing");
556
557 return -ECANCELED;
558 }
559 }
560
561 return 0;
562 }
563
bt_bap_base_subgroup_bis_codec_to_codec_cfg(const struct bt_bap_base_subgroup_bis * bis,struct bt_audio_codec_cfg * codec_cfg)564 int bt_bap_base_subgroup_bis_codec_to_codec_cfg(const struct bt_bap_base_subgroup_bis *bis,
565 struct bt_audio_codec_cfg *codec_cfg)
566 {
567 CHECKIF(bis == NULL) {
568 LOG_DBG("bis is NULL");
569
570 return -EINVAL;
571 }
572
573 CHECKIF(codec_cfg == NULL) {
574 LOG_DBG("codec_cfg is NULL");
575
576 return -EINVAL;
577 }
578
579 if (bis->data_len > ARRAY_SIZE(codec_cfg->data)) {
580 LOG_DBG("Cannot fit %u octets of codec data (max %zu)", bis->data_len,
581 ARRAY_SIZE(codec_cfg->data));
582
583 return -ENOMEM;
584 }
585
586 codec_cfg->data_len = bis->data_len;
587 memcpy(codec_cfg->data, bis->data, bis->data_len);
588
589 return 0;
590 }
591
base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis * bis,void * user_data)592 static bool base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis *bis, void *user_data)
593 {
594 uint32_t *base_bis_index_bitfield = user_data;
595
596 *base_bis_index_bitfield |= BT_ISO_BIS_INDEX_BIT(bis->index);
597
598 return true;
599 }
600
base_subgroup_cb(const struct bt_bap_base_subgroup * subgroup,void * user_data)601 static bool base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
602 {
603 const int err = bt_bap_base_subgroup_foreach_bis(subgroup, base_subgroup_bis_cb, user_data);
604
605 if (err != 0) {
606 LOG_DBG("Failed to parse all BIS: %d", err);
607 return false;
608 }
609
610 return true;
611 }
612
bt_bap_base_subgroup_get_bis_indexes(const struct bt_bap_base_subgroup * subgroup,uint32_t * bis_indexes)613 int bt_bap_base_subgroup_get_bis_indexes(const struct bt_bap_base_subgroup *subgroup,
614 uint32_t *bis_indexes)
615 {
616 CHECKIF(subgroup == NULL) {
617 LOG_DBG("subgroup is NULL");
618
619 return -EINVAL;
620 }
621
622 CHECKIF(bis_indexes == NULL) {
623 LOG_DBG("bis_indexes is NULL");
624
625 return -EINVAL;
626 }
627
628 *bis_indexes = 0U;
629
630 return bt_bap_base_subgroup_foreach_bis(subgroup, base_subgroup_bis_cb, bis_indexes);
631 }
632
bt_bap_base_get_bis_indexes(const struct bt_bap_base * base,uint32_t * bis_indexes)633 int bt_bap_base_get_bis_indexes(const struct bt_bap_base *base, uint32_t *bis_indexes)
634 {
635 CHECKIF(base == NULL) {
636 LOG_DBG("base is NULL");
637
638 return -EINVAL;
639 }
640
641 CHECKIF(bis_indexes == NULL) {
642 LOG_DBG("bis_indexes is NULL");
643
644 return -EINVAL;
645 }
646
647 *bis_indexes = 0U;
648
649 return bt_bap_base_foreach_subgroup(base, base_subgroup_cb, bis_indexes);
650 }
651