1 /*
2  * Copyright 2019 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
3  * www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #pragma once
19 
20 #include <bluetooth/log.h>
21 
22 #include <array>
23 #include <map>
24 #include <optional>
25 #include <ostream>
26 #include <vector>
27 
28 #include "types/raw_address.h"
29 
30 namespace bluetooth {
31 namespace le_audio {
32 
33 enum class LeAudioHealthBasedAction {
34   NONE = 0,
35   DISABLE,
36   CONSIDER_DISABLING,
37   INACTIVATE_GROUP,
38 };
39 
40 inline std::ostream& operator<<(std::ostream& os, const LeAudioHealthBasedAction action) {
41   switch (action) {
42     case LeAudioHealthBasedAction::NONE:
43       os << "NONE";
44       break;
45     case LeAudioHealthBasedAction::DISABLE:
46       os << "DISABLE";
47       break;
48     case LeAudioHealthBasedAction::CONSIDER_DISABLING:
49       os << "CONSIDER_DISABLING";
50       break;
51     case LeAudioHealthBasedAction::INACTIVATE_GROUP:
52       os << "INACTIVATE_GROUP";
53       break;
54     default:
55       os << "UNKNOWN";
56       break;
57   }
58   return os;
59 }
60 
61 enum class ConnectionState { DISCONNECTED = 0, CONNECTING, CONNECTED, DISCONNECTING };
62 
63 enum class GroupStatus {
64   INACTIVE = 0,
65   ACTIVE,
66   TURNED_IDLE_DURING_CALL,
67 };
68 
69 enum class GroupStreamStatus {
70   IDLE = 0,
71   STREAMING,
72   RELEASING,
73   SUSPENDING,
74   SUSPENDED,
75   CONFIGURED_AUTONOMOUS,
76   CONFIGURED_BY_USER,
77   DESTROYED,
78 };
79 
80 enum class GroupNodeStatus {
81   ADDED = 1,
82   REMOVED,
83 };
84 
85 enum class UnicastMonitorModeStatus {
86   STREAMING_REQUESTED = 0,
87   STREAMING,
88   STREAMING_SUSPENDED,
89   STREAMING_REQUESTED_NO_CONTEXT_VALIDATE,
90 };
91 
92 typedef enum {
93   LE_AUDIO_CODEC_INDEX_SOURCE_LC3 = 0,
94   LE_AUDIO_CODEC_INDEX_SOURCE_INVALID = 1000 * 1000,
95 } btle_audio_codec_index_t;
96 
97 typedef enum { QUALITY_STANDARD = 0, QUALITY_HIGH } btle_audio_quality_t;
98 
99 typedef enum {
100   LE_AUDIO_SAMPLE_RATE_INDEX_NONE = 0,
101   LE_AUDIO_SAMPLE_RATE_INDEX_8000HZ = 0x01 << 0,
102   LE_AUDIO_SAMPLE_RATE_INDEX_11025HZ = 0x01 << 1,
103   LE_AUDIO_SAMPLE_RATE_INDEX_16000HZ = 0x01 << 2,
104   LE_AUDIO_SAMPLE_RATE_INDEX_22050HZ = 0x01 << 3,
105   LE_AUDIO_SAMPLE_RATE_INDEX_24000HZ = 0x01 << 4,
106   LE_AUDIO_SAMPLE_RATE_INDEX_32000HZ = 0x01 << 5,
107   LE_AUDIO_SAMPLE_RATE_INDEX_44100HZ = 0x01 << 6,
108   LE_AUDIO_SAMPLE_RATE_INDEX_48000HZ = 0x01 << 7,
109   LE_AUDIO_SAMPLE_RATE_INDEX_88200HZ = 0x01 << 8,
110   LE_AUDIO_SAMPLE_RATE_INDEX_96000HZ = 0x01 << 9,
111   LE_AUDIO_SAMPLE_RATE_INDEX_176400HZ = 0x01 << 10,
112   LE_AUDIO_SAMPLE_RATE_INDEX_192000HZ = 0x01 << 11,
113   LE_AUDIO_SAMPLE_RATE_INDEX_384000HZ = 0x01 << 12
114 } btle_audio_sample_rate_index_t;
115 
116 typedef enum {
117   LE_AUDIO_BITS_PER_SAMPLE_INDEX_NONE = 0,
118   LE_AUDIO_BITS_PER_SAMPLE_INDEX_16 = 0x01 << 0,
119   LE_AUDIO_BITS_PER_SAMPLE_INDEX_24 = 0x01 << 1,
120   LE_AUDIO_BITS_PER_SAMPLE_INDEX_32 = 0x01 << 3
121 } btle_audio_bits_per_sample_index_t;
122 
123 typedef enum {
124   LE_AUDIO_CHANNEL_COUNT_INDEX_NONE = 0,
125   LE_AUDIO_CHANNEL_COUNT_INDEX_1 = 0x01 << 0,
126   LE_AUDIO_CHANNEL_COUNT_INDEX_2 = 0x01 << 1
127 } btle_audio_channel_count_index_t;
128 
129 typedef enum {
130   LE_AUDIO_FRAME_DURATION_INDEX_NONE = 0,
131   LE_AUDIO_FRAME_DURATION_INDEX_7500US = 0x01 << 0,
132   LE_AUDIO_FRAME_DURATION_INDEX_10000US = 0x01 << 1
133 } btle_audio_frame_duration_index_t;
134 
135 typedef struct btle_audio_codec_config {
136   btle_audio_codec_index_t codec_type = LE_AUDIO_CODEC_INDEX_SOURCE_INVALID;
137   btle_audio_sample_rate_index_t sample_rate = LE_AUDIO_SAMPLE_RATE_INDEX_NONE;
138   btle_audio_bits_per_sample_index_t bits_per_sample = LE_AUDIO_BITS_PER_SAMPLE_INDEX_NONE;
139   btle_audio_channel_count_index_t channel_count = LE_AUDIO_CHANNEL_COUNT_INDEX_NONE;
140   btle_audio_frame_duration_index_t frame_duration = LE_AUDIO_FRAME_DURATION_INDEX_NONE;
141   uint16_t octets_per_frame = 0;
142   int32_t codec_priority = 0;
143 
144   bool operator!=(const btle_audio_codec_config& other) const {
145     if (codec_type != other.codec_type) {
146       return true;
147     }
148     if (sample_rate != other.sample_rate) {
149       return true;
150     }
151     if (bits_per_sample != other.bits_per_sample) {
152       return true;
153     }
154     if (channel_count != other.channel_count) {
155       return true;
156     }
157     if (frame_duration != other.frame_duration) {
158       return true;
159     }
160     if (octets_per_frame != other.octets_per_frame) {
161       return true;
162     }
163     if (codec_priority != other.codec_priority) {
164       return true;
165     }
166     return false;
167   }
168   bool operator==(const btle_audio_codec_config& other) const { return !(*this != other); }
169 
ToStringbtle_audio_codec_config170   std::string ToString() const {
171     std::string codec_name_str;
172     std::string sample_rate_str;
173     std::string bits_per_sample_str;
174     std::string channel_count_str;
175     std::string frame_duration_str;
176     std::string octets_per_frame_str;
177     std::string codec_priority_str;
178 
179     switch (codec_type) {
180       case LE_AUDIO_CODEC_INDEX_SOURCE_LC3:
181         codec_name_str = "LC3";
182         break;
183       default:
184         codec_name_str = "Unknown LE codec " + std::to_string(codec_type);
185         break;
186     }
187 
188     switch (sample_rate) {
189       case LE_AUDIO_SAMPLE_RATE_INDEX_NONE:
190         sample_rate_str = "none";
191         break;
192       case LE_AUDIO_SAMPLE_RATE_INDEX_8000HZ:
193         sample_rate_str = "8000 hz";
194         break;
195       case LE_AUDIO_SAMPLE_RATE_INDEX_11025HZ:
196         sample_rate_str = "11025 hz";
197         break;
198       case LE_AUDIO_SAMPLE_RATE_INDEX_16000HZ:
199         sample_rate_str = "16000 hz";
200         break;
201       case LE_AUDIO_SAMPLE_RATE_INDEX_22050HZ:
202         sample_rate_str = "22050 hz";
203         break;
204       case LE_AUDIO_SAMPLE_RATE_INDEX_24000HZ:
205         sample_rate_str = "24000 hz";
206         break;
207       case LE_AUDIO_SAMPLE_RATE_INDEX_32000HZ:
208         sample_rate_str = "32000 hz";
209         break;
210       case LE_AUDIO_SAMPLE_RATE_INDEX_44100HZ:
211         sample_rate_str = "44100 hz";
212         break;
213       case LE_AUDIO_SAMPLE_RATE_INDEX_48000HZ:
214         sample_rate_str = "48000 hz";
215         break;
216       case LE_AUDIO_SAMPLE_RATE_INDEX_88200HZ:
217         sample_rate_str = "88200 hz";
218         break;
219       case LE_AUDIO_SAMPLE_RATE_INDEX_96000HZ:
220         sample_rate_str = "96000 hz";
221         break;
222       case LE_AUDIO_SAMPLE_RATE_INDEX_176400HZ:
223         sample_rate_str = "176400 hz";
224         break;
225       case LE_AUDIO_SAMPLE_RATE_INDEX_192000HZ:
226         sample_rate_str = "192000 hz";
227         break;
228       case LE_AUDIO_SAMPLE_RATE_INDEX_384000HZ:
229         sample_rate_str = "384000 hz";
230         break;
231       default:
232         sample_rate_str = "Unknown LE sample rate " + std::to_string(sample_rate);
233         break;
234     }
235 
236     switch (bits_per_sample) {
237       case LE_AUDIO_BITS_PER_SAMPLE_INDEX_NONE:
238         bits_per_sample_str = "none";
239         break;
240       case LE_AUDIO_BITS_PER_SAMPLE_INDEX_16:
241         bits_per_sample_str = "16";
242         break;
243       case LE_AUDIO_BITS_PER_SAMPLE_INDEX_24:
244         bits_per_sample_str = "24";
245         break;
246       case LE_AUDIO_BITS_PER_SAMPLE_INDEX_32:
247         bits_per_sample_str = "32";
248         break;
249       default:
250         bits_per_sample_str = "Unknown LE bits per sample " + std::to_string(bits_per_sample);
251         break;
252     }
253 
254     switch (channel_count) {
255       case LE_AUDIO_CHANNEL_COUNT_INDEX_NONE:
256         channel_count_str = "none";
257         break;
258       case LE_AUDIO_CHANNEL_COUNT_INDEX_1:
259         channel_count_str = "1";
260         break;
261       case LE_AUDIO_CHANNEL_COUNT_INDEX_2:
262         channel_count_str = "2";
263         break;
264       default:
265         channel_count_str = "Unknown LE channel count " + std::to_string(channel_count);
266         break;
267     }
268 
269     switch (frame_duration) {
270       case LE_AUDIO_FRAME_DURATION_INDEX_NONE:
271         frame_duration_str = "none";
272         break;
273       case LE_AUDIO_FRAME_DURATION_INDEX_7500US:
274         frame_duration_str = "7500 us";
275         break;
276       case LE_AUDIO_FRAME_DURATION_INDEX_10000US:
277         frame_duration_str = "10000 us";
278         break;
279       default:
280         frame_duration_str = "Unknown LE frame duration " + std::to_string(frame_duration);
281         break;
282     }
283 
284     if (octets_per_frame < 0) {
285       octets_per_frame_str = "Unknown LE octets per frame " + std::to_string(octets_per_frame);
286     } else {
287       octets_per_frame_str = std::to_string(octets_per_frame);
288     }
289 
290     if (codec_priority < -1) {
291       codec_priority_str = "Unknown LE codec priority " + std::to_string(codec_priority);
292     } else {
293       codec_priority_str = std::to_string(codec_priority);
294     }
295 
296     return "codec: " + codec_name_str + ", sample rate: " + sample_rate_str +
297            ", bits per sample: " + bits_per_sample_str + ", channel count: " + channel_count_str +
298            ", frame duration: " + frame_duration_str +
299            ", octets per frame: " + octets_per_frame_str + ", codec priroty: " + codec_priority_str;
300   }
301 } btle_audio_codec_config_t;
302 
303 class LeAudioClientCallbacks {
304 public:
305   virtual ~LeAudioClientCallbacks() = default;
306 
307   /* Callback to notify Java that stack is ready */
308   virtual void OnInitialized(void) = 0;
309 
310   /** Callback for profile connection state change */
311   virtual void OnConnectionState(ConnectionState state, const RawAddress& address) = 0;
312 
313   /* Callback with group status update */
314   virtual void OnGroupStatus(int group_id, GroupStatus group_status) = 0;
315 
316   /* Callback with node status update */
317   virtual void OnGroupNodeStatus(const RawAddress& bd_addr, int group_id,
318                                  GroupNodeStatus node_status) = 0;
319   /* Callback for newly recognized or reconfigured existing le audio group */
320   virtual void OnAudioConf(uint8_t direction, int group_id, uint32_t snk_audio_location,
321                            uint32_t src_audio_location, uint16_t avail_cont) = 0;
322   /* Callback for sink audio location recognized */
323   virtual void OnSinkAudioLocationAvailable(const RawAddress& address,
324                                             uint32_t snk_audio_locations) = 0;
325   /* Callback with local codec capabilities */
326   virtual void OnAudioLocalCodecCapabilities(
327           std::vector<btle_audio_codec_config_t> local_input_capa_codec_conf,
328           std::vector<btle_audio_codec_config_t> local_output_capa_codec_conf) = 0;
329   /* Callback with current group codec configurations. Should change when PACs
330    * changes */
331   virtual void OnAudioGroupCurrentCodecConf(int group_id,
332                                             btle_audio_codec_config_t input_codec_conf,
333                                             btle_audio_codec_config_t output_codec_conf) = 0;
334   /* Callback with selectable group codec configurations. Should change when
335    * context changes */
336   virtual void OnAudioGroupSelectableCodecConf(
337           int group_id, std::vector<btle_audio_codec_config_t> input_selectable_codec_conf,
338           std::vector<btle_audio_codec_config_t> output_selectable_codec_conf) = 0;
339   virtual void OnHealthBasedRecommendationAction(const RawAddress& address,
340                                                  LeAudioHealthBasedAction action) = 0;
341   virtual void OnHealthBasedGroupRecommendationAction(int group_id,
342                                                       LeAudioHealthBasedAction action) = 0;
343 
344   virtual void OnUnicastMonitorModeStatus(uint8_t direction, UnicastMonitorModeStatus status) = 0;
345 
346   /* Callback with group stream status update */
347   virtual void OnGroupStreamStatus(int group_id, GroupStreamStatus group_stream_status) = 0;
348 };
349 
350 class LeAudioClientInterface {
351 public:
352   virtual ~LeAudioClientInterface() = default;
353 
354   /* Register the LeAudio callbacks */
355   virtual void Initialize(LeAudioClientCallbacks* callbacks,
356                           const std::vector<btle_audio_codec_config_t>& offloading_preference) = 0;
357 
358   /** Connect to LEAudio */
359   virtual void Connect(const RawAddress& address) = 0;
360 
361   /** Disconnect from LEAudio */
362   virtual void Disconnect(const RawAddress& address) = 0;
363 
364   /* Set enable/disable State for the LeAudio device */
365   virtual void SetEnableState(const RawAddress& address, bool enabled) = 0;
366 
367   /* Cleanup the LeAudio */
368   virtual void Cleanup(void) = 0;
369 
370   /* Called when LeAudio is unbonded. */
371   virtual void RemoveDevice(const RawAddress& address) = 0;
372 
373   /* Attach le audio node to group */
374   virtual void GroupAddNode(int group_id, const RawAddress& addr) = 0;
375 
376   /* Detach le audio node from a group */
377   virtual void GroupRemoveNode(int group_id, const RawAddress& addr) = 0;
378 
379   /* Set active le audio group */
380   virtual void GroupSetActive(int group_id) = 0;
381 
382   /* Set codec config preference */
383   virtual void SetCodecConfigPreference(int group_id, btle_audio_codec_config_t input_codec_config,
384                                         btle_audio_codec_config_t output_codec_config) = 0;
385 
386   /* Set Ccid for context type */
387   virtual void SetCcidInformation(int ccid, int context_type) = 0;
388 
389   /* Set In call flag */
390   virtual void SetInCall(bool in_call) = 0;
391 
392   /* Set Sink listening mode flag */
393   virtual void SetUnicastMonitorMode(uint8_t direction, bool enable) = 0;
394 
395   /* Sends a preferred audio profiles change */
396   virtual void SendAudioProfilePreferences(int group_id, bool is_output_preference_le_audio,
397                                            bool is_duplex_preference_le_audio) = 0;
398 
399   /* Set allowed to stream context */
400   virtual void SetGroupAllowedContextMask(int group_id, int sink_context_types,
401                                           int source_context_types) = 0;
402 };
403 
404 /* Represents the broadcast source state. */
405 enum class BroadcastState {
406   STOPPED = 0,
407   CONFIGURING,
408   CONFIGURED,
409   ENABLING,
410   DISABLING,
411   STOPPING,
412   STREAMING,
413 };
414 
415 using BroadcastId = uint32_t;
416 static constexpr BroadcastId kBroadcastIdInvalid = 0x00000000;
417 using BroadcastCode = std::array<uint8_t, 16>;
418 
419 /* Content Metadata LTV Types */
420 constexpr uint8_t kLeAudioMetadataTypePreferredAudioContext = 0x01;
421 constexpr uint8_t kLeAudioMetadataTypeStreamingAudioContext = 0x02;
422 constexpr uint8_t kLeAudioMetadataTypeProgramInfo = 0x03;
423 constexpr uint8_t kLeAudioMetadataTypeLanguage = 0x04;
424 constexpr uint8_t kLeAudioMetadataTypeCcidList = 0x05;
425 
426 /* Codec specific LTV Types */
427 constexpr uint8_t kLeAudioLtvTypeSamplingFreq = 0x01;
428 constexpr uint8_t kLeAudioLtvTypeFrameDuration = 0x02;
429 constexpr uint8_t kLeAudioLtvTypeAudioChannelAllocation = 0x03;
430 constexpr uint8_t kLeAudioLtvTypeOctetsPerCodecFrame = 0x04;
431 constexpr uint8_t kLeAudioLtvTypeCodecFrameBlocksPerSdu = 0x05;
432 
433 /* Audio quality configuration in public broadcast announcement */
434 constexpr uint8_t kLeAudioQualityStandard = 0x1 << 1;
435 constexpr uint8_t kLeAudioQualityHigh = 0x1 << 2;
436 
437 /* Unknown RSSI value 0x7F - 127 */
438 constexpr uint8_t kLeAudioSourceRssiUnknown = 0x7F;
439 
440 struct BasicAudioAnnouncementCodecConfig {
441   /* 5 octets for the Codec ID */
442   uint8_t codec_id;
443   uint16_t vendor_company_id;
444   uint16_t vendor_codec_id;
445 
446   /* Codec params - series of LTV formatted triplets */
447   std::map<uint8_t, std::vector<uint8_t>> codec_specific_params;
448   std::optional<std::vector<uint8_t>> vendor_codec_specific_params;
449 };
450 
451 struct BasicAudioAnnouncementBisConfig {
452   std::map<uint8_t, std::vector<uint8_t>> codec_specific_params;
453   std::optional<std::vector<uint8_t>> vendor_codec_specific_params;
454 
455   uint8_t bis_index;
456 };
457 
458 struct BasicAudioAnnouncementSubgroup {
459   /* Subgroup specific codec configuration and metadata */
460   BasicAudioAnnouncementCodecConfig codec_config;
461   // Content metadata
462   std::map<uint8_t, std::vector<uint8_t>> metadata;
463   // Broadcast channel configuration
464   std::vector<BasicAudioAnnouncementBisConfig> bis_configs;
465 };
466 
467 struct BasicAudioAnnouncementData {
468   /* Announcement Header fields */
469   uint32_t presentation_delay_us;
470 
471   /* Subgroup specific configurations */
472   std::vector<BasicAudioAnnouncementSubgroup> subgroup_configs;
473 };
474 
475 struct PublicBroadcastAnnouncementData {
476   // Public Broadcast Announcement features bitmap
477   uint8_t features;
478   // Metadata
479   std::map<uint8_t, std::vector<uint8_t>> metadata;
480 };
481 
482 struct BroadcastMetadata {
483   bool is_public;
484   uint16_t pa_interval;
485   RawAddress addr;
486   uint8_t addr_type;
487   uint8_t adv_sid;
488 
489   BroadcastId broadcast_id;
490   std::string broadcast_name;
491   std::optional<BroadcastCode> broadcast_code;
492 
493   PublicBroadcastAnnouncementData public_announcement;
494   /* Presentation delay and subgroup configurations */
495   BasicAudioAnnouncementData basic_audio_announcement;
496 };
497 
498 class LeAudioBroadcasterCallbacks {
499 public:
500   virtual ~LeAudioBroadcasterCallbacks() = default;
501   /* Callback for the newly created broadcast event. */
502   virtual void OnBroadcastCreated(uint32_t broadcast_id, bool success) = 0;
503 
504   /* Callback for the destroyed broadcast event. */
505   virtual void OnBroadcastDestroyed(uint32_t broadcast_id) = 0;
506   /* Callback for the broadcast source state event. */
507   virtual void OnBroadcastStateChanged(uint32_t broadcast_id, BroadcastState state) = 0;
508   /* Callback for the broadcast metadata change. */
509   virtual void OnBroadcastMetadataChanged(uint32_t broadcast_id,
510                                           const BroadcastMetadata& broadcast_metadata) = 0;
511   /* Callback for broadcast audio session create event. */
512   virtual void OnBroadcastAudioSessionCreated(bool success) = 0;
513 };
514 
515 class LeAudioBroadcasterInterface {
516 public:
517   virtual ~LeAudioBroadcasterInterface() = default;
518   /* Register the LeAudio Broadcaster callbacks */
519   virtual void Initialize(LeAudioBroadcasterCallbacks* callbacks) = 0;
520   /* Stop the LeAudio Broadcaster and all active broadcasts */
521   virtual void Stop(void) = 0;
522   /* Cleanup the LeAudio Broadcaster */
523   virtual void Cleanup(void) = 0;
524   /* Create Broadcast instance */
525   virtual void CreateBroadcast(bool is_public, std::string broadcast_name,
526                                std::optional<BroadcastCode> broadcast_code,
527                                std::vector<uint8_t> public_metadata,
528                                std::vector<uint8_t> subgroup_quality,
529                                std::vector<std::vector<uint8_t>> subgroup_metadata) = 0;
530   /* Update the ongoing Broadcast metadata */
531   virtual void UpdateMetadata(uint32_t broadcast_id, std::string broadcast_name,
532                               std::vector<uint8_t> public_metadata,
533                               std::vector<std::vector<uint8_t>> subgroup_metadata) = 0;
534 
535   /* Start the existing Broadcast stream */
536   virtual void StartBroadcast(uint32_t broadcast_id) = 0;
537   /* Pause the ongoing Broadcast stream */
538   virtual void PauseBroadcast(uint32_t broadcast_id) = 0;
539   /* Stop the Broadcast (no stream, no periodic advertisements */
540   virtual void StopBroadcast(uint32_t broadcast_id) = 0;
541   /* Destroy the existing Broadcast instance */
542   virtual void DestroyBroadcast(uint32_t broadcast_id) = 0;
543   /* Get Broadcast Metadata */
544   virtual void GetBroadcastMetadata(uint32_t broadcast_id) = 0;
545 };
546 
547 } /* namespace le_audio */
548 } /* namespace bluetooth */
549 
550 namespace std {
551 template <>
552 struct formatter<bluetooth::le_audio::btle_audio_codec_index_t>
553     : enum_formatter<bluetooth::le_audio::btle_audio_codec_index_t> {};
554 template <>
555 struct formatter<bluetooth::le_audio::btle_audio_sample_rate_index_t>
556     : enum_formatter<bluetooth::le_audio::btle_audio_sample_rate_index_t> {};
557 template <>
558 struct formatter<bluetooth::le_audio::btle_audio_bits_per_sample_index_t>
559     : enum_formatter<bluetooth::le_audio::btle_audio_bits_per_sample_index_t> {};
560 template <>
561 struct formatter<bluetooth::le_audio::btle_audio_channel_count_index_t>
562     : enum_formatter<bluetooth::le_audio::btle_audio_channel_count_index_t> {};
563 template <>
564 struct formatter<bluetooth::le_audio::btle_audio_frame_duration_index_t>
565     : enum_formatter<bluetooth::le_audio::btle_audio_frame_duration_index_t> {};
566 template <>
567 struct formatter<bluetooth::le_audio::GroupStreamStatus>
568     : enum_formatter<bluetooth::le_audio::GroupStreamStatus> {};
569 }  // namespace std
570