blob: 447c3f62e2b6af2310fb68438adc5ecd93dda5eb [file] [log] [blame]
/******************************************************************************
*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include "bta_closure_api.h"
#include "bta_bap_uclient_api.h"
#include "btif_common.h"
#include "btif_storage.h"
#include "osi/include/thread.h"
// Capabilities to be stored on CodecConfig structure :
// Supported_Sampling_Frequencies  ( codec config sampling rate )
// Audio_Channel_Counts ( codec config channel mode )
// Supported_Frame_Durations ( 1st byte of codec specific 1 ) 
// Max_Supported_LC3_Frames_Per_SDU  ( 2nd byte of codec specific 1)
// Preferred_Audio_Contexts ( 3&4 bytes of codec specific 1 )
// Supported_Octets_Per_Codec_Frame ( first 4 bytes codec specific 2 )
/* Vendor_Specific Metadata ( first 4 bytes of codec specific 3)
* 1st byte conveys LC3Q support,
* 2nd byte conveys LC3Q version
*/
// Configurations to be stored in CodecConfig structure :
// Sampling_Frequency  ( codec config sampling rate )
// Audio_Channel_Allocation ( codec config channel mode )
// Frame_Duration  ( 5th bye of codec specific 1 ) 
// LC3_Blocks_Per_SDU ( 6th byte of codec specific 1 )
// Preferred_Audio_Contexts ( 7&8 bytes of codec specific 1 )
// Octets_Per_Codec_Frame ( 2 bytes  )  ( 5&6th bytes of codec specific 2 )
// LC3Q preferred (1 byte) ( 7th byte of codec specific 2 )
/* Vendor_Specific Metadata ( first 4 bytes of codec specific 3)
* 1st byte conveys LC3Q support,
* 2nd byte conveys LC3Q version
*/
#include <base/logging.h>
#include <hardware/bluetooth.h>
#include <hardware/bt_pacs_client.h>
using bluetooth::bap::pacs::CodecConfig;
using bluetooth::bap::pacs::CodecChannelMode;
constexpr uint8_t CAPA_SUP_FRAME_DUR_INDEX = 0x00; // CS1
constexpr uint8_t CAPA_MAX_SUP_LC3_FRAMES_INDEX = 0x01; // CS1
constexpr uint8_t CAPA_PREF_AUDIO_CONT_INDEX = 0x02; // CS1
constexpr uint8_t CAPA_SUP_OCTS_PER_FRAME_INDEX = 0x00; // CS2
constexpr uint8_t CAPA_VENDOR_METADATA_LC3Q_PREF_INDEX = 0x00; // CS3
constexpr uint8_t CAPA_VENDOR_METADATA_LC3Q_VER_INDEX = 0x01; // CS3
constexpr uint8_t CONFIG_FRAME_DUR_INDEX = 0x04; // CS1
constexpr uint8_t CONFIG_LC3_BLOCKS_INDEX = 0x05; // CS1
constexpr uint8_t CONFIG_PREF_AUDIO_CONT_INDEX = 0x06; // CS1
constexpr uint8_t CONFIG_OCTS_PER_FRAME_INDEX = 0x04; // CS2
constexpr uint8_t CONFIG_LC3Q_PREF_INDEX = 0x06; // CS2
constexpr uint8_t CONFIG_VENDOR_METADATA_LC3Q_PREF_INDEX = 0x00; // CS3
constexpr uint8_t CONFIG_VENDOR_METADATA_LC3Q_VER_INDEX = 0x01; // CS3
// capabilities
bool UpdateCapaSupFrameDurations(CodecConfig *config , uint8_t sup_frame) {
config->codec_specific_1 &= ~(0xFF << (CAPA_SUP_FRAME_DUR_INDEX * 8));
config->codec_specific_1 |= sup_frame << (CAPA_SUP_FRAME_DUR_INDEX * 8);
return true;
}
bool UpdateCapaMaxSupLc3Frames(CodecConfig *config,
uint8_t max_sup_lc3_frames) {
config->codec_specific_1 &= ~(0xFF << (CAPA_MAX_SUP_LC3_FRAMES_INDEX * 8));
config->codec_specific_1 |= max_sup_lc3_frames <<
(CAPA_MAX_SUP_LC3_FRAMES_INDEX * 8);
return true;
}
bool UpdateCapaPreferredContexts(CodecConfig *config, uint16_t contexts) {
config->codec_specific_1 &= ~(0xFFFF << (CAPA_PREF_AUDIO_CONT_INDEX * 8));
config->codec_specific_1 |= contexts << (CAPA_PREF_AUDIO_CONT_INDEX * 8);
return true;
}
bool UpdateCapaSupOctsPerFrame(CodecConfig *config,
uint32_t octs_per_frame) {
config->codec_specific_2 &= ~(0xFFFFFFFF <<
(CAPA_SUP_OCTS_PER_FRAME_INDEX * 8));
config->codec_specific_2 |= octs_per_frame <<
(CAPA_SUP_OCTS_PER_FRAME_INDEX * 8);
return true;
}
bool UpdateCapaVendorMetaDataLc3QPref(CodecConfig *config, bool lc3q_pref) {
config->codec_specific_3 &= ~(0xFF << (CAPA_VENDOR_METADATA_LC3Q_PREF_INDEX * 8));
config->codec_specific_3 |= lc3q_pref << (CAPA_VENDOR_METADATA_LC3Q_PREF_INDEX * 8);
return true;
}
bool UpdateCapaVendorMetaDataLc3QVer(CodecConfig *config, uint8_t lc3q_ver) {
config->codec_specific_3 &= ~(0xFF << (CAPA_VENDOR_METADATA_LC3Q_VER_INDEX * 8));
config->codec_specific_3 |= lc3q_ver << (CAPA_VENDOR_METADATA_LC3Q_VER_INDEX * 8);
return true;
}
uint8_t GetCapaSupFrameDurations(CodecConfig *config) {
return (config->codec_specific_1 >> (8*CAPA_SUP_FRAME_DUR_INDEX)) & 0xff;
}
uint8_t GetCapaMaxSupLc3Frames(CodecConfig *config) {
if (((config->codec_specific_1 >>
(8*CAPA_MAX_SUP_LC3_FRAMES_INDEX)) & 0xff) == 0x0) {
uint8_t max_chnl_count = 0;
LOG(ERROR) << __func__
<< ": Max Sup LC3 frames is 0, deriving based on chnl count";
if(static_cast<uint16_t> (config->channel_mode) &
static_cast<uint16_t> (CodecChannelMode::CODEC_CHANNEL_MODE_STEREO)) {
max_chnl_count = 2;
} else if(static_cast<uint16_t> (config->channel_mode) &
static_cast<uint16_t> (CodecChannelMode::CODEC_CHANNEL_MODE_MONO)) {
max_chnl_count = 1;
}
return max_chnl_count;
} else {
return (config->codec_specific_1 >>
(8*CAPA_MAX_SUP_LC3_FRAMES_INDEX)) & 0xff;
}
}
uint16_t GetCapaPreferredContexts(CodecConfig *config) {
return (config->codec_specific_1 >>
(8*CAPA_PREF_AUDIO_CONT_INDEX)) & 0xffff;
}
uint32_t GetCapaSupOctsPerFrame(CodecConfig *config) {
return (config->codec_specific_2 >>
(8*CAPA_SUP_OCTS_PER_FRAME_INDEX)) & 0xffffffff;
}
bool GetCapaVendorMetaDataLc3QPref(CodecConfig *config) {
if (((config->codec_specific_3 >>
(8*CAPA_VENDOR_METADATA_LC3Q_PREF_INDEX)) & 0xff) == 0x0) {
return false;
} else
return true;
}
uint8_t GetCapaVendorMetaDataLc3QVer(CodecConfig *config) {
return (config->codec_specific_3 >>
(8*CAPA_VENDOR_METADATA_LC3Q_VER_INDEX)) & 0xff;
}
// Configurations
bool UpdateFrameDuration(CodecConfig *config , uint8_t frame_dur) {
uint64_t value = 0xFF;
config->codec_specific_1 &= ~(value << (CONFIG_FRAME_DUR_INDEX*8));
config->codec_specific_1 |= static_cast<uint64_t>(frame_dur) <<
(CONFIG_FRAME_DUR_INDEX * 8);
return true;
}
bool UpdateLc3BlocksPerSdu(CodecConfig *config, uint8_t lc3_blocks_per_sdu) {
uint64_t value = 0xFF;
config->codec_specific_1 &= ~(value << (CONFIG_LC3_BLOCKS_INDEX * 8));
config->codec_specific_1 |= static_cast<uint64_t>(lc3_blocks_per_sdu) <<
(CONFIG_LC3_BLOCKS_INDEX * 8);
return true;
}
bool UpdatePreferredAudioContext(CodecConfig *config ,
uint16_t pref_audio_context) {
uint64_t value = 0xFFFF;
config->codec_specific_1 &= ~(value << (CONFIG_PREF_AUDIO_CONT_INDEX*8));
config->codec_specific_1 |= static_cast<uint64_t>(pref_audio_context) <<
(CONFIG_PREF_AUDIO_CONT_INDEX * 8);
return true;
}
bool UpdateOctsPerFrame(CodecConfig *config , uint16_t octs_per_frame) {
uint64_t value = 0xFFFF;
config->codec_specific_2 &= ~(value << (CONFIG_OCTS_PER_FRAME_INDEX * 8));
config->codec_specific_2 |=
static_cast<uint64_t>(octs_per_frame) <<
(CONFIG_OCTS_PER_FRAME_INDEX * 8);
return true;
}
bool UpdateLc3QPreference(CodecConfig *config , bool lc3q_pref) {
uint64_t value = 0xFF;
config->codec_specific_2 &= ~(value << (CONFIG_LC3Q_PREF_INDEX * 8));
config->codec_specific_2 |=
static_cast<uint64_t>(lc3q_pref) <<
(CONFIG_LC3Q_PREF_INDEX * 8);
LOG(WARNING) << __func__
<< ": lc3q_pref cs2: " << loghex(config->codec_specific_2);
return true;
}
bool UpdateVendorMetaDataLc3QPref(CodecConfig *config, bool lc3q_pref) {
uint64_t value = 0xFF;
config->codec_specific_3 &= ~(value << (CONFIG_VENDOR_METADATA_LC3Q_PREF_INDEX * 8));
config->codec_specific_3 |= static_cast<uint64_t>(lc3q_pref) <<
(CONFIG_VENDOR_METADATA_LC3Q_PREF_INDEX * 8);
return true;
}
bool UpdateVendorMetaDataLc3QVer(CodecConfig *config, uint8_t lc3q_ver) {
uint64_t value = 0xFF;
config->codec_specific_3 &= ~(value << (CONFIG_VENDOR_METADATA_LC3Q_VER_INDEX * 8));
config->codec_specific_3 |= static_cast<uint64_t>(lc3q_ver) <<
(CONFIG_VENDOR_METADATA_LC3Q_VER_INDEX * 8);
return true;
}
uint8_t GetFrameDuration(CodecConfig *config) {
return (config->codec_specific_1 >> (8*CONFIG_FRAME_DUR_INDEX)) & 0xff;
}
uint8_t GetLc3BlocksPerSdu(CodecConfig *config) {
return (config->codec_specific_1 >> (8*CONFIG_LC3_BLOCKS_INDEX)) & 0xff;
}
uint16_t GetPreferredAudioContext(CodecConfig *config) {
return (config->codec_specific_1 >>
(8*CONFIG_PREF_AUDIO_CONT_INDEX)) & 0xffff;
}
uint16_t GetOctsPerFrame(CodecConfig *config) {
return (config->codec_specific_2 >> (8*CONFIG_OCTS_PER_FRAME_INDEX)) & 0xffff;
}
uint8_t GetLc3QPreference(CodecConfig *config) {
LOG(WARNING) << __func__ << ": lc3q_pref cs2: "
<< loghex((config->codec_specific_2 >> (8*CONFIG_LC3Q_PREF_INDEX)) & 0xff);
return (config->codec_specific_2 >>
(8*CONFIG_LC3Q_PREF_INDEX)) & 0xff;
}
uint8_t GetVendorMetaDataLc3QPref(CodecConfig *config) {
return (config->codec_specific_3 >>
(8*CONFIG_VENDOR_METADATA_LC3Q_PREF_INDEX)) & 0xff;
}
uint8_t GetVendorMetaDataLc3QVer(CodecConfig *config) {
return (config->codec_specific_3 >>
(8*CONFIG_VENDOR_METADATA_LC3Q_VER_INDEX)) & 0xff;
}
bool IsCodecConfigEqual(CodecConfig *src_config, CodecConfig *dst_config) {
// first check if passed codec configs are configurations or
// capabilities using first byte of codec specific 1
if(src_config == nullptr || dst_config == nullptr) {
return false;
}
bool is_src_capability = src_config->codec_specific_1 & 0XFF;
bool is_dst_capability = dst_config->codec_specific_1 & 0XFF;
// check the codec type
if(src_config->codec_type != dst_config->codec_type) {
LOG(ERROR) << __func__ << ": No match for codec type ";
return false;
}
// check sample rate
if(src_config->sample_rate != dst_config->sample_rate) {
LOG(ERROR) << __func__ << ": No match for sample rate";
return false;
}
// check channel mode
if(!(static_cast<int>(src_config->channel_mode) &
static_cast<int>(dst_config->channel_mode))) {
LOG(ERROR) << __func__ << ": No match for channel mode ";
return false;
}
LOG(WARNING) << __func__
<< ": is_src_capability: " << loghex(is_src_capability)
<< ", is_dst_capability: " << loghex(is_dst_capability);
if(is_src_capability && is_dst_capability) {
if(src_config->codec_specific_1 != dst_config->codec_specific_1 ||
src_config->codec_specific_2 != dst_config->codec_specific_2 ||
src_config->codec_specific_3 != dst_config->codec_specific_3) {
LOG(WARNING) << __func__ << ": No match for CS params. ";
return false;
}
} else if (!is_src_capability && !is_dst_capability) {
LOG(INFO) << __func__ << ": Comparison for both configs ";
uint8_t src_frame_dur = GetFrameDuration(src_config);
uint8_t src_lc3_blocks_per_sdu = GetLc3BlocksPerSdu(src_config);
uint16_t src_octs_per_frame = GetOctsPerFrame(src_config);
uint8_t src_lc3q_pref = GetLc3QPreference(src_config);
uint16_t src_pref_audio_context = GetPreferredAudioContext(src_config);
uint8_t dst_frame_dur = GetFrameDuration(dst_config);
uint8_t dst_lc3_blocks_per_sdu = GetLc3BlocksPerSdu(dst_config);
uint16_t dst_octs_per_frame = GetOctsPerFrame(dst_config);
uint8_t dst_lc3q_pref = GetLc3QPreference(dst_config);
uint16_t dst_pref_audio_context = GetPreferredAudioContext(dst_config);
if(src_frame_dur != dst_frame_dur) {
LOG(ERROR) << __func__ << ": Frame Dur not match with existing config ";
return false;
}
if(src_lc3_blocks_per_sdu != dst_lc3_blocks_per_sdu) {
LOG(ERROR) << __func__ << ": Lc3 blocks not match with existing config ";
return false;
}
if(src_octs_per_frame != dst_octs_per_frame) {
LOG(ERROR) << __func__ << ": Octs per frame not match with existing config ";
return false;
}
if(src_lc3q_pref != dst_lc3q_pref) {
LOG(ERROR) << __func__ << ": Lc3Q pref not match with existing config ";
return false;
}
if (!(src_pref_audio_context & dst_pref_audio_context)) {
LOG(ERROR) << __func__ << ": pref_audio_context not match with existing config ";
return false;
}
} else if(is_src_capability || is_dst_capability) {
CodecConfig *capa_config = is_src_capability ? src_config:dst_config;
CodecConfig *oth_config = is_src_capability ? dst_config:src_config;
uint8_t capa_sup_frames = GetCapaSupFrameDurations(capa_config);
uint8_t capa_max_sup_lc3_frames = GetCapaMaxSupLc3Frames(capa_config);
uint16_t capa_min_sup_octs = GetCapaSupOctsPerFrame(capa_config) & 0xFFFF;
uint16_t capa_max_sup_octs = (GetCapaSupOctsPerFrame(capa_config)
& 0xFFFF0000) >> 16;
bool capa_lc3q_pref = GetCapaVendorMetaDataLc3QPref(capa_config);
uint16_t capa_pref_audio_context = GetCapaPreferredContexts(capa_config);
uint8_t frame_dur = GetFrameDuration(oth_config);
uint8_t lc3_blocks_per_sdu = GetLc3BlocksPerSdu(oth_config);
uint16_t octs_per_frame = GetOctsPerFrame(oth_config);
uint8_t lc3q_pref = GetLc3QPreference(oth_config);
uint16_t dst_pref_audio_context = GetPreferredAudioContext(oth_config);
LOG(WARNING) << __func__
<< ": capa_sup_frames: " << loghex(capa_sup_frames)
<< ", frame_dur: " << loghex(frame_dur);
LOG(WARNING) << __func__
<< ": capa_lc3q_pref: " << capa_lc3q_pref
<< ", lc3q_pref: " << loghex(lc3q_pref);
LOG(WARNING) << __func__
<< ": capa_pref_audio_context: " << capa_pref_audio_context
<< ", dst_pref_audio_context: " << dst_pref_audio_context;
if(!(capa_sup_frames & (0x01 << frame_dur))) {
LOG(ERROR) << __func__ << ": No match for frame duration ";
return false;
}
if(capa_max_sup_lc3_frames && lc3_blocks_per_sdu) {
if(capa_max_sup_lc3_frames < lc3_blocks_per_sdu *
static_cast<uint8_t> (oth_config->channel_mode)) {
LOG(ERROR) << __func__ << ": blocks per sdu exceeds the capacity ";
return false;
}
}
if( octs_per_frame < capa_min_sup_octs ||
octs_per_frame > capa_max_sup_octs) {
LOG(ERROR) << __func__ << ": octs per frame not in limits ";
return true;
}
if (!(capa_pref_audio_context & dst_pref_audio_context)) {
LOG(ERROR) << __func__ << ": No Match for Audio context";
return false;
}
}
return true;
}