zephyr/subsys/bluetooth/audio/shell/audio.h
Emil Gydesen d229b89a2b Bluetooth: BAP: Shell: Add tracking of empty SDUs
There are devices that will send empty SDUs instead of
empty PDUs, and while that should be harmless, it is nice
to know what is going on, and we may want to deal with
empty SDUs and empty PDUs differently.

This also modifies the state for TS and PSN to only be
applied for valid ISO packets.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2024-04-08 09:12:58 -04:00

1094 lines
34 KiB
C

/** @file
* @brief Bluetooth audio shell functions
*
* This is not to be included by the application.
*/
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __AUDIO_H
#define __AUDIO_H
#include <stdio.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/printk.h>
#include "shell/bt.h"
#define SHELL_PRINT_INDENT_LEVEL_SIZE 2
extern struct bt_csip_set_member_svc_inst *svc_inst;
ssize_t audio_ad_data_add(struct bt_data *data, const size_t data_size, const bool discoverable,
const bool connectable);
ssize_t audio_pa_data_add(struct bt_data *data_array, const size_t data_array_size);
ssize_t csis_ad_data_add(struct bt_data *data, const size_t data_size, const bool discoverable);
size_t cap_acceptor_ad_data_add(struct bt_data data[], size_t data_size, bool discoverable);
size_t gmap_ad_data_add(struct bt_data data[], size_t data_size);
size_t pbp_ad_data_add(struct bt_data data[], size_t data_size);
ssize_t cap_initiator_ad_data_add(struct bt_data *data_array, const size_t data_array_size,
const bool discoverable, const bool connectable);
ssize_t cap_initiator_pa_data_add(struct bt_data *data_array, const size_t data_array_size);
#if defined(CONFIG_BT_AUDIO)
/* Must guard before including audio.h as audio.h uses Kconfigs guarded by
* CONFIG_BT_AUDIO
*/
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
#include <zephyr/bluetooth/audio/cap.h>
#define LOCATION BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT
#define CONTEXT \
(BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED | BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL | \
BT_AUDIO_CONTEXT_TYPE_MEDIA | \
COND_CODE_1(IS_ENABLED(CONFIG_BT_GMAP), (BT_AUDIO_CONTEXT_TYPE_GAME), (0)))
const struct named_lc3_preset *gmap_get_named_preset(bool is_unicast, enum bt_audio_dir dir,
const char *preset_arg);
struct named_lc3_preset {
const char *name;
struct bt_bap_lc3_preset preset;
};
const struct named_lc3_preset *bap_get_named_preset(bool is_unicast, enum bt_audio_dir dir,
const char *preset_arg);
struct shell_stream {
struct bt_cap_stream stream;
struct bt_audio_codec_cfg codec_cfg;
struct bt_audio_codec_qos qos;
#if defined(CONFIG_LIBLC3)
uint32_t lc3_freq_hz;
uint32_t lc3_frame_duration_us;
uint16_t lc3_octets_per_frame;
uint8_t lc3_frames_per_sdu;
#endif /* CONFIG_LIBLC3 */
#if defined(CONFIG_BT_AUDIO_TX)
int64_t connected_at_ticks; /* The uptime tick measured when stream was connected */
uint16_t seq_num;
struct k_work_delayable audio_send_work;
bool tx_active;
#if defined(CONFIG_LIBLC3)
atomic_t lc3_enqueue_cnt;
size_t lc3_sdu_cnt;
#endif /* CONFIG_LIBLC3 */
#endif /* CONFIG_BT_AUDIO_TX */
#if defined(CONFIG_BT_AUDIO_RX)
struct bt_iso_recv_info last_info;
size_t empty_sdu_pkts;
size_t lost_pkts;
size_t err_pkts;
size_t dup_psn;
size_t rx_cnt;
size_t dup_ts;
#endif /* CONFIG_BT_AUDIO_RX */
};
struct broadcast_source {
bool is_cap;
union {
struct bt_bap_broadcast_source *bap_source;
struct bt_cap_broadcast_source *cap_source;
};
struct bt_audio_codec_cfg codec_cfg;
struct bt_audio_codec_qos qos;
};
struct broadcast_sink {
struct bt_bap_broadcast_sink *bap_sink;
struct bt_le_per_adv_sync *pa_sync;
uint8_t received_base[UINT8_MAX];
uint8_t base_size;
uint32_t broadcast_id;
size_t stream_cnt;
bool syncable;
};
#define BAP_UNICAST_AC_MAX_CONN 2U
#define BAP_UNICAST_AC_MAX_SNK (2U * BAP_UNICAST_AC_MAX_CONN)
#define BAP_UNICAST_AC_MAX_SRC (2U * BAP_UNICAST_AC_MAX_CONN)
#define BAP_UNICAST_AC_MAX_PAIR MAX(BAP_UNICAST_AC_MAX_SNK, BAP_UNICAST_AC_MAX_SRC)
#define BAP_UNICAST_AC_MAX_STREAM (BAP_UNICAST_AC_MAX_SNK + BAP_UNICAST_AC_MAX_SRC)
#if defined(CONFIG_BT_BAP_UNICAST)
#define UNICAST_SERVER_STREAM_COUNT \
COND_CODE_1(CONFIG_BT_ASCS, (CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT), \
(0))
#define UNICAST_CLIENT_STREAM_COUNT \
COND_CODE_1(CONFIG_BT_BAP_UNICAST_CLIENT, \
(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT + \
CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT), \
(0))
extern struct shell_stream unicast_streams[CONFIG_BT_MAX_CONN * (UNICAST_SERVER_STREAM_COUNT +
UNICAST_CLIENT_STREAM_COUNT)];
#if defined(CONFIG_BT_BAP_UNICAST_CLIENT)
struct bap_unicast_ac_param {
char *name;
size_t conn_cnt;
size_t snk_cnt[BAP_UNICAST_AC_MAX_CONN];
size_t src_cnt[BAP_UNICAST_AC_MAX_CONN];
size_t snk_chan_cnt;
size_t src_chan_cnt;
};
extern struct bt_bap_unicast_group *default_unicast_group;
extern struct bt_bap_ep *snks[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT];
extern struct bt_bap_ep *srcs[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT];
extern struct named_lc3_preset default_sink_preset;
extern struct named_lc3_preset default_source_preset;
int bap_ac_create_unicast_group(const struct bap_unicast_ac_param *param,
struct shell_stream *snk_uni_streams[], size_t snk_cnt,
struct shell_stream *src_uni_streams[], size_t src_cnt);
int cap_ac_unicast(const struct shell *sh, size_t argc, char **argv,
const struct bap_unicast_ac_param *param);
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
#endif /* CONFIG_BT_BAP_UNICAST */
static inline void print_qos(const struct shell *sh, const struct bt_audio_codec_qos *qos)
{
#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) || defined(CONFIG_BT_BAP_UNICAST)
shell_print(sh,
"QoS: interval %u framing 0x%02x phy 0x%02x sdu %u rtn %u latency %u pd %u",
qos->interval, qos->framing, qos->phy, qos->sdu, qos->rtn, qos->latency,
qos->pd);
#else
shell_print(sh, "QoS: interval %u framing 0x%02x phy 0x%02x sdu %u rtn %u pd %u",
qos->interval, qos->framing, qos->phy, qos->sdu, qos->rtn, qos->pd);
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE || CONFIG_BT_BAP_UNICAST */
}
struct print_ltv_info {
const struct shell *sh;
size_t indent;
size_t cnt;
};
static bool print_ltv_elem(struct bt_data *data, void *user_data)
{
struct print_ltv_info *ltv_info = user_data;
const size_t elem_indent = ltv_info->indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
shell_print(ltv_info->sh, "%*s#%zu: type 0x%02x value_len %u", ltv_info->indent, "",
ltv_info->cnt, data->type, data->data_len);
shell_fprintf(ltv_info->sh, SHELL_NORMAL, "%*s", elem_indent, "");
for (uint8_t i = 0U; i < data->data_len; i++) {
shell_fprintf(ltv_info->sh, SHELL_NORMAL, "%02X", data->data[i]);
}
shell_fprintf(ltv_info->sh, SHELL_NORMAL, "\n");
ltv_info->cnt++;
return true;
}
static void print_ltv_array(const struct shell *sh, size_t indent, const uint8_t *ltv_data,
size_t ltv_data_len)
{
struct print_ltv_info ltv_info = {
.sh = sh,
.cnt = 0U,
.indent = indent,
};
bt_audio_data_parse(ltv_data, ltv_data_len, print_ltv_elem, &ltv_info);
}
static inline char *context_bit_to_str(enum bt_audio_context context)
{
switch (context) {
case BT_AUDIO_CONTEXT_TYPE_PROHIBITED:
return "Prohibited";
case BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED:
return "Unspecified";
case BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL:
return "Converstation";
case BT_AUDIO_CONTEXT_TYPE_MEDIA:
return "Media";
case BT_AUDIO_CONTEXT_TYPE_GAME:
return "Game";
case BT_AUDIO_CONTEXT_TYPE_INSTRUCTIONAL:
return "Instructional";
case BT_AUDIO_CONTEXT_TYPE_VOICE_ASSISTANTS:
return "Voice assistant";
case BT_AUDIO_CONTEXT_TYPE_LIVE:
return "Live";
case BT_AUDIO_CONTEXT_TYPE_SOUND_EFFECTS:
return "Sound effects";
case BT_AUDIO_CONTEXT_TYPE_NOTIFICATIONS:
return "Notifications";
case BT_AUDIO_CONTEXT_TYPE_RINGTONE:
return "Ringtone";
case BT_AUDIO_CONTEXT_TYPE_ALERTS:
return "Alerts";
case BT_AUDIO_CONTEXT_TYPE_EMERGENCY_ALARM:
return "Emergency alarm";
default:
return "Unknown context";
}
}
static inline void print_codec_meta_pref_context(const struct shell *sh, size_t indent,
enum bt_audio_context context)
{
shell_print(sh, "%*sPreferred audio contexts:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
/* There can be up to 16 bits set in the field */
for (size_t i = 0U; i < 16; i++) {
const uint16_t bit_val = BIT(i);
if (context & bit_val) {
shell_print(sh, "%*s%s (0x%04X)", indent, "", context_bit_to_str(bit_val),
bit_val);
}
}
}
static inline void print_codec_meta_stream_context(const struct shell *sh, size_t indent,
enum bt_audio_context context)
{
shell_print(sh, "%*sStreaming audio contexts:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
/* There can be up to 16 bits set in the field */
for (size_t i = 0U; i < 16; i++) {
const uint16_t bit_val = BIT(i);
if (context & bit_val) {
shell_print(sh, "%*s%s (0x%04X)", indent, "", context_bit_to_str(bit_val),
bit_val);
}
}
}
static inline void print_codec_meta_program_info(const struct shell *sh, size_t indent,
const uint8_t *program_info,
uint8_t program_info_len)
{
shell_print(sh, "%*sProgram info:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
shell_fprintf(sh, SHELL_NORMAL, "%*s", indent, "");
for (uint8_t i = 0U; i < program_info_len; i++) {
shell_fprintf(sh, SHELL_NORMAL, "%c", (char)program_info[i]);
}
shell_fprintf(sh, SHELL_NORMAL, "\n");
}
static inline void print_codec_meta_language(const struct shell *sh, size_t indent,
uint32_t stream_lang)
{
uint8_t lang_array[3];
sys_put_be24(stream_lang, lang_array);
shell_print(sh, "%*sLanguage: %c%c%c", indent, "", (char)lang_array[0], (char)lang_array[1],
(char)lang_array[2]);
}
static inline void print_codec_meta_ccid_list(const struct shell *sh, size_t indent,
const uint8_t *ccid_list, uint8_t ccid_list_len)
{
shell_print(sh, "%*sCCID list:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
/* There can be up to 16 bits set in the field */
for (uint8_t i = 0U; i < ccid_list_len; i++) {
shell_print(sh, "%*s0x%02X ", indent, "", ccid_list[i]);
}
}
static inline char *parental_rating_to_str(enum bt_audio_parental_rating parental_rating)
{
switch (parental_rating) {
case BT_AUDIO_PARENTAL_RATING_NO_RATING:
return "No rating";
case BT_AUDIO_PARENTAL_RATING_AGE_ANY:
return "Any";
case BT_AUDIO_PARENTAL_RATING_AGE_5_OR_ABOVE:
return "Age 5 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_6_OR_ABOVE:
return "Age 6 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_7_OR_ABOVE:
return "Age 7 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_8_OR_ABOVE:
return "Age 8 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_9_OR_ABOVE:
return "Age 9 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_10_OR_ABOVE:
return "Age 10 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_11_OR_ABOVE:
return "Age 11 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_12_OR_ABOVE:
return "Age 12 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_13_OR_ABOVE:
return "Age 13 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_14_OR_ABOVE:
return "Age 14 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_15_OR_ABOVE:
return "Age 15 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_16_OR_ABOVE:
return "Age 16 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_17_OR_ABOVE:
return "Age 17 or above";
case BT_AUDIO_PARENTAL_RATING_AGE_18_OR_ABOVE:
return "Age 18 or above";
default:
return "Unknown rating";
}
}
static inline void print_codec_meta_parental_rating(const struct shell *sh, size_t indent,
enum bt_audio_parental_rating parental_rating)
{
shell_print(sh, "%*sRating: %s (0x%02X)", indent, "",
parental_rating_to_str(parental_rating), (uint8_t)parental_rating);
}
static inline void print_codec_meta_program_info_uri(const struct shell *sh, size_t indent,
const uint8_t *program_info_uri,
uint8_t program_info_uri_len)
{
shell_print(sh, "%*sProgram info URI:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
shell_fprintf(sh, SHELL_NORMAL, "%*s", indent, "");
for (uint8_t i = 0U; i < program_info_uri_len; i++) {
shell_fprintf(sh, SHELL_NORMAL, "%c", (char)program_info_uri[i]);
}
shell_fprintf(sh, SHELL_NORMAL, "\n");
}
static inline void print_codec_meta_audio_active_state(const struct shell *sh, size_t indent,
enum bt_audio_active_state state)
{
shell_print(sh, "%*sAudio active state: %s (0x%02X)", indent, "",
state == BT_AUDIO_ACTIVE_STATE_ENABLED ? "enabled" : "disabled",
(uint8_t)state);
}
static inline void print_codec_meta_bcast_audio_immediate_rend_flag(const struct shell *sh,
size_t indent)
{
shell_print(sh, "%*sBroadcast audio immediate rendering flag set", indent, "");
}
static inline void print_codec_meta_extended(const struct shell *sh, size_t indent,
const uint8_t *extended_meta, size_t extended_meta_len)
{
shell_print(sh, "%*sExtended metadata:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
shell_fprintf(sh, SHELL_NORMAL, "%*s", indent, "");
for (uint8_t i = 0U; i < extended_meta_len; i++) {
shell_fprintf(sh, SHELL_NORMAL, "%u", (uint8_t)extended_meta[i]);
}
shell_fprintf(sh, SHELL_NORMAL, "\n");
}
static inline void print_codec_meta_vendor(const struct shell *sh, size_t indent,
const uint8_t *vendor_meta, size_t vender_meta_len)
{
shell_print(sh, "%*sVendor metadata:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
shell_fprintf(sh, SHELL_NORMAL, "%*s", indent, "");
for (uint8_t i = 0U; i < vender_meta_len; i++) {
shell_fprintf(sh, SHELL_NORMAL, "%u", (uint8_t)vendor_meta[i]);
}
shell_fprintf(sh, SHELL_NORMAL, "\n");
}
static inline char *codec_cap_freq_bit_to_str(enum bt_audio_codec_cap_freq freq)
{
switch (freq) {
case BT_AUDIO_CODEC_CAP_FREQ_8KHZ:
return "8000 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_11KHZ:
return "11025 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_16KHZ:
return "16000 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_22KHZ:
return "22050 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_24KHZ:
return "24000 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_32KHZ:
return "32000 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_44KHZ:
return "44100 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_48KHZ:
return "48000 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_88KHZ:
return "88200 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_96KHZ:
return "96000 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_176KHZ:
return "176400 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_192KHZ:
return "192000 Hz";
case BT_AUDIO_CODEC_CAP_FREQ_384KHZ:
return "384000 Hz";
default:
return "Unknown supported frequency";
}
}
static inline void print_codec_cap_freq(const struct shell *sh, size_t indent,
enum bt_audio_codec_cap_freq freq)
{
shell_print(sh, "%*sSupported sampling frequencies:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
/* There can be up to 16 bits set in the field */
for (size_t i = 0; i < 16; i++) {
const uint16_t bit_val = BIT(i);
if (freq & bit_val) {
shell_print(sh, "%*s%s (0x%04X)", indent, "",
codec_cap_freq_bit_to_str(bit_val), bit_val);
}
}
}
static inline char *codec_cap_frame_dur_bit_to_str(enum bt_audio_codec_cap_frame_dur frame_dur)
{
switch (frame_dur) {
case BT_AUDIO_CODEC_CAP_DURATION_7_5:
return "7.5 ms";
case BT_AUDIO_CODEC_CAP_DURATION_10:
return "10 ms";
case BT_AUDIO_CODEC_CAP_DURATION_PREFER_7_5:
return "7.5 ms preferred";
case BT_AUDIO_CODEC_CAP_DURATION_PREFER_10:
return "10 ms preferred";
default:
return "Unknown frame duration";
}
}
static inline void print_codec_cap_frame_dur(const struct shell *sh, size_t indent,
enum bt_audio_codec_cap_frame_dur frame_dur)
{
shell_print(sh, "%*sSupported frame durations:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
/* There can be up to 8 bits set in the field */
for (size_t i = 0; i < 8; i++) {
const uint8_t bit_val = BIT(i);
if (frame_dur & bit_val) {
shell_print(sh, "%*s%s (0x%02X)", indent, "",
codec_cap_frame_dur_bit_to_str(bit_val), bit_val);
}
}
}
static inline char *codec_cap_chan_count_bit_to_str(enum bt_audio_codec_cap_chan_count chan_count)
{
switch (chan_count) {
case BT_AUDIO_CODEC_CAP_CHAN_COUNT_1:
return "1 channel";
case BT_AUDIO_CODEC_CAP_CHAN_COUNT_2:
return "2 channels";
case BT_AUDIO_CODEC_CAP_CHAN_COUNT_3:
return "3 channels";
case BT_AUDIO_CODEC_CAP_CHAN_COUNT_4:
return "4 channels";
case BT_AUDIO_CODEC_CAP_CHAN_COUNT_5:
return "5 channels";
case BT_AUDIO_CODEC_CAP_CHAN_COUNT_6:
return "6 channels";
case BT_AUDIO_CODEC_CAP_CHAN_COUNT_7:
return "7 channels";
case BT_AUDIO_CODEC_CAP_CHAN_COUNT_8:
return "8 channels";
default:
return "Unknown channel count";
}
}
static inline void print_codec_cap_chan_count(const struct shell *sh, size_t indent,
enum bt_audio_codec_cap_chan_count chan_count)
{
shell_print(sh, "%*sSupported channel counts:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
/* There can be up to 8 bits set in the field */
for (size_t i = 0; i < 8; i++) {
const uint8_t bit_val = BIT(i);
if (chan_count & bit_val) {
shell_print(sh, "%*s%s (0x%02X)", indent, "",
codec_cap_chan_count_bit_to_str(bit_val), bit_val);
}
}
}
static inline void print_codec_cap_octets_per_codec_frame(
const struct shell *sh, size_t indent,
const struct bt_audio_codec_octets_per_codec_frame *codec_frame)
{
shell_print(sh, "%*sSupported octets per codec frame counts:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
shell_print(sh, "%*sMin: %u", indent, "", codec_frame->min);
shell_print(sh, "%*sMax: %u", indent, "", codec_frame->max);
}
static inline void print_codec_cap_max_codec_frames_per_sdu(const struct shell *sh, size_t indent,
uint8_t codec_frames_per_sdu)
{
shell_print(sh, "%*sSupported max codec frames per SDU: %u", indent, "",
codec_frames_per_sdu);
}
static inline void print_codec_cap(const struct shell *sh, size_t indent,
const struct bt_audio_codec_cap *codec_cap)
{
shell_print(sh, "%*scodec cap id 0x%02x cid 0x%04x vid 0x%04x", indent, "", codec_cap->id,
codec_cap->cid, codec_cap->vid);
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
#if CONFIG_BT_AUDIO_CODEC_CAP_MAX_DATA_SIZE > 0
shell_print(sh, "%*sCodec specific capabilities:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
if (codec_cap->data_len == 0U) {
shell_print(sh, "%*sNone", indent, "");
} else if (codec_cap->id == BT_HCI_CODING_FORMAT_LC3) {
struct bt_audio_codec_octets_per_codec_frame codec_frame;
int ret;
ret = bt_audio_codec_cap_get_freq(codec_cap);
if (ret >= 0) {
print_codec_cap_freq(sh, indent, (enum bt_audio_codec_cap_freq)ret);
}
ret = bt_audio_codec_cap_get_frame_dur(codec_cap);
if (ret >= 0) {
print_codec_cap_frame_dur(sh, indent,
(enum bt_audio_codec_cap_frame_dur)ret);
}
ret = bt_audio_codec_cap_get_supported_audio_chan_counts(codec_cap);
if (ret >= 0) {
print_codec_cap_chan_count(sh, indent,
(enum bt_audio_codec_cap_chan_count)ret);
}
ret = bt_audio_codec_cap_get_octets_per_frame(codec_cap, &codec_frame);
if (ret >= 0) {
print_codec_cap_octets_per_codec_frame(sh, indent, &codec_frame);
}
ret = bt_audio_codec_cap_get_max_codec_frames_per_sdu(codec_cap);
if (ret >= 0) {
print_codec_cap_max_codec_frames_per_sdu(sh, indent, (uint8_t)ret);
}
} else { /* If not LC3, we cannot assume it's LTV */
shell_fprintf(sh, SHELL_NORMAL, "%*s", indent, "");
for (uint8_t i = 0U; i < codec_cap->data_len; i++) {
shell_fprintf(sh, SHELL_NORMAL, "%*s%02X", indent, "", codec_cap->data[i]);
}
shell_fprintf(sh, SHELL_NORMAL, "\n");
}
/* Reduce for metadata*/
indent -= SHELL_PRINT_INDENT_LEVEL_SIZE;
#endif /* CONFIG_BT_AUDIO_CODEC_CAP_MAX_DATA_SIZE > 0 */
#if CONFIG_BT_AUDIO_CODEC_CAP_MAX_METADATA_SIZE > 0
shell_print(sh, "%*sCodec capabilities metadata:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
if (codec_cap->meta_len == 0U) {
shell_print(sh, "%*sNone", indent, "");
} else {
const uint8_t *data;
int ret;
ret = bt_audio_codec_cap_meta_get_pref_context(codec_cap);
if (ret >= 0) {
print_codec_meta_pref_context(sh, indent, (enum bt_audio_context)ret);
}
ret = bt_audio_codec_cap_meta_get_stream_context(codec_cap);
if (ret >= 0) {
print_codec_meta_stream_context(sh, indent, (enum bt_audio_context)ret);
}
ret = bt_audio_codec_cap_meta_get_program_info(codec_cap, &data);
if (ret >= 0) {
print_codec_meta_program_info(sh, indent, data, (uint8_t)ret);
}
ret = bt_audio_codec_cap_meta_get_stream_lang(codec_cap);
if (ret >= 0) {
print_codec_meta_language(sh, indent, (uint32_t)ret);
}
ret = bt_audio_codec_cap_meta_get_ccid_list(codec_cap, &data);
if (ret >= 0) {
print_codec_meta_ccid_list(sh, indent, data, (uint8_t)ret);
}
ret = bt_audio_codec_cap_meta_get_parental_rating(codec_cap);
if (ret >= 0) {
print_codec_meta_parental_rating(sh, indent,
(enum bt_audio_parental_rating)ret);
}
ret = bt_audio_codec_cap_meta_get_audio_active_state(codec_cap);
if (ret >= 0) {
print_codec_meta_audio_active_state(sh, indent,
(enum bt_audio_active_state)ret);
}
ret = bt_audio_codec_cap_meta_get_bcast_audio_immediate_rend_flag(codec_cap);
if (ret >= 0) {
print_codec_meta_bcast_audio_immediate_rend_flag(sh, indent);
}
ret = bt_audio_codec_cap_meta_get_extended(codec_cap, &data);
if (ret >= 0) {
print_codec_meta_extended(sh, indent, data, (uint8_t)ret);
}
ret = bt_audio_codec_cap_meta_get_vendor(codec_cap, &data);
if (ret >= 0) {
print_codec_meta_vendor(sh, indent, data, (uint8_t)ret);
}
}
#endif /* CONFIG_BT_AUDIO_CODEC_CAP_MAX_METADATA_SIZE > 0 */
}
static inline void print_codec_cfg_freq(const struct shell *sh, size_t indent,
enum bt_audio_codec_cfg_freq freq)
{
shell_print(sh, "%*sSampling frequency: %u Hz (0x%04X)", indent, "",
bt_audio_codec_cfg_freq_to_freq_hz(freq), (uint16_t)freq);
}
static inline void print_codec_cfg_frame_dur(const struct shell *sh, size_t indent,
enum bt_audio_codec_cfg_frame_dur frame_dur)
{
shell_print(sh, "%*sFrame duration: %u us (0x%02X)", indent, "",
bt_audio_codec_cfg_frame_dur_to_frame_dur_us(frame_dur), (uint8_t)frame_dur);
}
static inline char *chan_location_bit_to_str(enum bt_audio_location chan_allocation)
{
switch (chan_allocation) {
case BT_AUDIO_LOCATION_MONO_AUDIO:
return "Mono";
case BT_AUDIO_LOCATION_FRONT_LEFT:
return "Front left";
case BT_AUDIO_LOCATION_FRONT_RIGHT:
return "Front right";
case BT_AUDIO_LOCATION_FRONT_CENTER:
return "Front center";
case BT_AUDIO_LOCATION_LOW_FREQ_EFFECTS_1:
return "Low frequency effects 1";
case BT_AUDIO_LOCATION_BACK_LEFT:
return "Back left";
case BT_AUDIO_LOCATION_BACK_RIGHT:
return "Back right";
case BT_AUDIO_LOCATION_FRONT_LEFT_OF_CENTER:
return "Front left of center";
case BT_AUDIO_LOCATION_FRONT_RIGHT_OF_CENTER:
return "Front right of center";
case BT_AUDIO_LOCATION_BACK_CENTER:
return "Back center";
case BT_AUDIO_LOCATION_LOW_FREQ_EFFECTS_2:
return "Low frequency effects 2";
case BT_AUDIO_LOCATION_SIDE_LEFT:
return "Side left";
case BT_AUDIO_LOCATION_SIDE_RIGHT:
return "Side right";
case BT_AUDIO_LOCATION_TOP_FRONT_LEFT:
return "Top front left";
case BT_AUDIO_LOCATION_TOP_FRONT_RIGHT:
return "Top front right";
case BT_AUDIO_LOCATION_TOP_FRONT_CENTER:
return "Top front center";
case BT_AUDIO_LOCATION_TOP_CENTER:
return "Top center";
case BT_AUDIO_LOCATION_TOP_BACK_LEFT:
return "Top back left";
case BT_AUDIO_LOCATION_TOP_BACK_RIGHT:
return "Top back right";
case BT_AUDIO_LOCATION_TOP_SIDE_LEFT:
return "Top side left";
case BT_AUDIO_LOCATION_TOP_SIDE_RIGHT:
return "Top side right";
case BT_AUDIO_LOCATION_TOP_BACK_CENTER:
return "Top back center";
case BT_AUDIO_LOCATION_BOTTOM_FRONT_CENTER:
return "Bottom front center";
case BT_AUDIO_LOCATION_BOTTOM_FRONT_LEFT:
return "Bottom front left";
case BT_AUDIO_LOCATION_BOTTOM_FRONT_RIGHT:
return "Bottom front right";
case BT_AUDIO_LOCATION_FRONT_LEFT_WIDE:
return "Front left wide";
case BT_AUDIO_LOCATION_FRONT_RIGHT_WIDE:
return "Front right wde";
case BT_AUDIO_LOCATION_LEFT_SURROUND:
return "Left surround";
case BT_AUDIO_LOCATION_RIGHT_SURROUND:
return "Right surround";
default:
return "Unknown location";
}
}
static inline void print_codec_cfg_chan_allocation(const struct shell *sh, size_t indent,
enum bt_audio_location chan_allocation)
{
shell_print(sh, "%*sChannel allocation:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
/* There can be up to 32 bits set in the field */
for (size_t i = 0; i < 32; i++) {
const uint8_t bit_val = BIT(i);
if (chan_allocation & bit_val) {
shell_print(sh, "%*s%s (0x%08X)", indent, "",
chan_location_bit_to_str(bit_val), bit_val);
}
}
}
static inline void print_codec_cfg_octets_per_frame(const struct shell *sh, size_t indent,
uint16_t octets_per_frame)
{
shell_print(sh, "%*sOctets per codec frame: %u", indent, "", octets_per_frame);
}
static inline void print_codec_cfg_frame_blocks_per_sdu(const struct shell *sh, size_t indent,
uint8_t frame_blocks_per_sdu)
{
shell_print(sh, "%*sCodec frame blocks per SDU: %u", indent, "", frame_blocks_per_sdu);
}
static inline void print_codec_cfg(const struct shell *sh, size_t indent,
const struct bt_audio_codec_cfg *codec_cfg)
{
shell_print(sh, "%*scodec cfg id 0x%02x cid 0x%04x vid 0x%04x count %u", indent, "",
codec_cfg->id, codec_cfg->cid, codec_cfg->vid, codec_cfg->data_len);
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0
shell_print(sh, "%*sCodec specific configuration:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
if (codec_cfg->data_len == 0U) {
shell_print(sh, "%*sNone", indent, "");
} else if (codec_cfg->id == BT_HCI_CODING_FORMAT_LC3) {
enum bt_audio_location chan_allocation;
int ret;
ret = bt_audio_codec_cfg_get_freq(codec_cfg);
if (ret >= 0) {
print_codec_cfg_freq(sh, indent, (enum bt_audio_codec_cfg_freq)ret);
}
ret = bt_audio_codec_cfg_get_frame_dur(codec_cfg);
if (ret >= 0) {
print_codec_cfg_frame_dur(sh, indent,
(enum bt_audio_codec_cfg_frame_dur)ret);
}
ret = bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation);
if (ret >= 0) {
print_codec_cfg_chan_allocation(sh, indent, chan_allocation);
}
ret = bt_audio_codec_cfg_get_octets_per_frame(codec_cfg);
if (ret >= 0) {
print_codec_cfg_octets_per_frame(sh, indent, (uint16_t)ret);
}
ret = bt_audio_codec_cfg_get_frame_blocks_per_sdu(codec_cfg, false);
if (ret >= 0) {
print_codec_cfg_frame_blocks_per_sdu(sh, indent, (uint8_t)ret);
}
} else { /* If not LC3, we cannot assume it's LTV */
shell_fprintf(sh, SHELL_NORMAL, "%*s", indent, "");
for (uint8_t i = 0U; i < codec_cfg->data_len; i++) {
shell_fprintf(sh, SHELL_NORMAL, "%*s%02X", indent, "", codec_cfg->data[i]);
}
shell_fprintf(sh, SHELL_NORMAL, "\n");
}
/* Reduce for metadata*/
indent -= SHELL_PRINT_INDENT_LEVEL_SIZE;
#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */
#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0
shell_print(sh, "%*sCodec specific metadata:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
if (codec_cfg->meta_len == 0U) {
shell_print(sh, "%*sNone", indent, "");
} else {
const uint8_t *data;
int ret;
ret = bt_audio_codec_cfg_meta_get_pref_context(codec_cfg);
if (ret >= 0) {
print_codec_meta_pref_context(sh, indent, (enum bt_audio_context)ret);
}
ret = bt_audio_codec_cfg_meta_get_stream_context(codec_cfg);
if (ret >= 0) {
print_codec_meta_stream_context(sh, indent, (enum bt_audio_context)ret);
}
ret = bt_audio_codec_cfg_meta_get_program_info(codec_cfg, &data);
if (ret >= 0) {
print_codec_meta_program_info(sh, indent, data, (uint8_t)ret);
}
ret = bt_audio_codec_cfg_meta_get_stream_lang(codec_cfg);
if (ret >= 0) {
print_codec_meta_language(sh, indent, (uint32_t)ret);
}
ret = bt_audio_codec_cfg_meta_get_ccid_list(codec_cfg, &data);
if (ret >= 0) {
print_codec_meta_ccid_list(sh, indent, data, (uint8_t)ret);
}
ret = bt_audio_codec_cfg_meta_get_parental_rating(codec_cfg);
if (ret >= 0) {
print_codec_meta_parental_rating(sh, indent,
(enum bt_audio_parental_rating)ret);
}
ret = bt_audio_codec_cfg_meta_get_audio_active_state(codec_cfg);
if (ret >= 0) {
print_codec_meta_audio_active_state(sh, indent,
(enum bt_audio_active_state)ret);
}
ret = bt_audio_codec_cfg_meta_get_bcast_audio_immediate_rend_flag(codec_cfg);
if (ret >= 0) {
print_codec_meta_bcast_audio_immediate_rend_flag(sh, indent);
}
ret = bt_audio_codec_cfg_meta_get_extended(codec_cfg, &data);
if (ret >= 0) {
print_codec_meta_extended(sh, indent, data, (uint8_t)ret);
}
ret = bt_audio_codec_cfg_meta_get_vendor(codec_cfg, &data);
if (ret >= 0) {
print_codec_meta_vendor(sh, indent, data, (uint8_t)ret);
}
}
#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0 */
}
#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
struct bap_broadcast_ac_param {
char *name;
size_t stream_cnt;
size_t chan_cnt;
};
int cap_ac_broadcast(const struct shell *sh, size_t argc, char **argv,
const struct bap_broadcast_ac_param *param);
extern struct shell_stream broadcast_source_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
extern struct broadcast_source default_source;
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
static inline bool print_base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis *bis,
void *user_data)
{
size_t indent = 2 * SHELL_PRINT_INDENT_LEVEL_SIZE;
struct bt_bap_base_codec_id *codec_id = user_data;
shell_print(ctx_shell, "%*sBIS index: 0x%02X", indent, "", bis->index);
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
/* Print CC data */
if (codec_id->id == BT_HCI_CODING_FORMAT_LC3) {
struct bt_audio_codec_cfg codec_cfg = {
.id = codec_id->id,
.cid = codec_id->cid,
.vid = codec_id->vid,
};
int err;
err = bt_bap_base_subgroup_bis_codec_to_codec_cfg(bis, &codec_cfg);
if (err == 0) {
print_codec_cfg(ctx_shell, indent, &codec_cfg);
} else {
shell_print(ctx_shell, "%*sCodec specific configuration:", indent, "");
print_ltv_array(ctx_shell, indent, bis->data, bis->data_len);
}
} else { /* If not LC3, we cannot assume it's LTV */
shell_print(ctx_shell, "%*sCodec specific configuration:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
shell_fprintf(ctx_shell, SHELL_NORMAL, "%*s", indent, "");
for (uint8_t i = 0U; i < bis->data_len; i++) {
shell_fprintf(ctx_shell, SHELL_NORMAL, "%02X", bis->data[i]);
}
shell_fprintf(ctx_shell, SHELL_NORMAL, "\n");
}
return true;
}
static inline bool print_base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup,
void *user_data)
{
size_t indent = 1 * SHELL_PRINT_INDENT_LEVEL_SIZE;
struct bt_bap_base_codec_id codec_id;
struct bt_audio_codec_cfg codec_cfg;
uint8_t *data;
int ret;
shell_print(ctx_shell, "Subgroup %p:", subgroup);
ret = bt_bap_base_get_subgroup_codec_id(subgroup, &codec_id);
if (ret < 0) {
return false;
}
shell_print(ctx_shell, "%*sCodec Format: 0x%02X", indent, "", codec_id.id);
shell_print(ctx_shell, "%*sCompany ID : 0x%04X", indent, "", codec_id.cid);
shell_print(ctx_shell, "%*sVendor ID : 0x%04X", indent, "", codec_id.vid);
ret = bt_bap_base_subgroup_codec_to_codec_cfg(subgroup, &codec_cfg);
if (ret == 0) {
print_codec_cfg(ctx_shell, indent, &codec_cfg);
} else {
/* If we cannot store it in a codec_cfg, then we cannot easily print it as such */
ret = bt_bap_base_get_subgroup_codec_data(subgroup, &data);
if (ret < 0) {
return false;
}
shell_print(ctx_shell, "%*sCodec specific configuration:", indent, "");
indent += SHELL_PRINT_INDENT_LEVEL_SIZE;
/* Print CC data */
if (codec_id.id == BT_HCI_CODING_FORMAT_LC3) {
print_ltv_array(ctx_shell, indent, data, (uint8_t)ret);
} else { /* If not LC3, we cannot assume it's LTV */
shell_fprintf(ctx_shell, SHELL_NORMAL, "%*s", indent, "");
for (uint8_t i = 0U; i < (uint8_t)ret; i++) {
shell_fprintf(ctx_shell, SHELL_NORMAL, "%c", data[i]);
}
shell_fprintf(ctx_shell, SHELL_NORMAL, "\n");
}
ret = bt_bap_base_get_subgroup_codec_meta(subgroup, &data);
if (ret < 0) {
return false;
}
shell_print(ctx_shell,
"%*sCodec specific metadata:", indent - SHELL_PRINT_INDENT_LEVEL_SIZE,
"");
/* Print metadata */
if (codec_id.id == BT_HCI_CODING_FORMAT_LC3) {
print_ltv_array(ctx_shell, indent, data, (uint8_t)ret);
} else { /* If not LC3, we cannot assume it's LTV */
shell_fprintf(ctx_shell, SHELL_NORMAL, "%*s", indent, "");
for (uint8_t i = 0U; i < (uint8_t)ret; i++) {
shell_fprintf(ctx_shell, SHELL_NORMAL, "%c", data[i]);
}
shell_fprintf(ctx_shell, SHELL_NORMAL, "\n");
}
}
ret = bt_bap_base_subgroup_foreach_bis(subgroup, print_base_subgroup_bis_cb, &codec_id);
if (ret < 0) {
return false;
}
return true;
}
static inline void print_base(const struct bt_bap_base *base)
{
int err;
shell_print(ctx_shell, "Presentation delay: %d", bt_bap_base_get_pres_delay(base));
shell_print(ctx_shell, "Subgroup count: %d", bt_bap_base_get_subgroup_count(base));
err = bt_bap_base_foreach_subgroup(base, print_base_subgroup_cb, NULL);
if (err < 0) {
shell_info(ctx_shell, "Invalid BASE: %d", err);
}
}
static inline void copy_unicast_stream_preset(struct shell_stream *stream,
const struct named_lc3_preset *named_preset)
{
memcpy(&stream->qos, &named_preset->preset.qos, sizeof(stream->qos));
memcpy(&stream->codec_cfg, &named_preset->preset.codec_cfg, sizeof(stream->codec_cfg));
}
static inline void copy_broadcast_source_preset(struct broadcast_source *source,
const struct named_lc3_preset *named_preset)
{
memcpy(&source->qos, &named_preset->preset.qos, sizeof(source->qos));
memcpy(&source->codec_cfg, &named_preset->preset.codec_cfg, sizeof(source->codec_cfg));
}
#endif /* CONFIG_BT_AUDIO */
#endif /* __AUDIO_H */