| /* Copyright (C) 2016-2017, The Linux Foundation. All rights reserved. |
| * |
| * Not a Contribution |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted (subject to the limitations in the |
| * disclaimer below) provided that the following conditions are met: |
| |
| * Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the following |
| disclaimer in the documentation and/or other materials provided |
| with the distribution. |
| |
| * Neither the name of The Linux Foundation nor the names of its |
| contributors may be used to endorse or promote products derived |
| from this software without specific prior written permission. |
| |
| * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE |
| * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT |
| * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| *****************************************************************************/ |
| /***************************************************************************** |
| * Copyright (C) 2009-2012 Broadcom Corporation |
| * |
| * 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. |
| * |
| ******************************************************************************/ |
| /* bthost_ipc.c |
| * |
| * Description: Implements IPC interface between HAL and BT host |
| * |
| *****************************************************************************/ |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include "ldac_level_bit_rate_lookup.h" |
| #include "bthost_ipc.h" |
| #include <errno.h> |
| #include <inttypes.h> |
| #include <pthread.h> |
| #include <stdint.h> |
| #include <sys/errno.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <system/audio.h> |
| #include <hardware/audio.h> |
| |
| #include <hardware/hardware.h> |
| #include <log/log.h> |
| #include <cutils/properties.h> |
| |
| #ifdef LOG_TAG |
| #undef LOG_TAG |
| #endif |
| #define LOG_TAG "bthost_ipc" |
| |
| bool DEBUG = false; |
| static int bt_split_a2dp_enabled = 0; |
| /***************************************************************************** |
| ** Constants & Macros |
| ******************************************************************************/ |
| /* Below two values adds up to 8 sec retry to address IOT issues*/ |
| #define STREAM_START_MAX_RETRY_COUNT 10 |
| #define STREAM_START_MAX_RETRY_LOOPER 8 |
| #define CTRL_CHAN_RETRY_COUNT 3 |
| #define CHECK_A2DP_READY_MAX_COUNT 20 |
| |
| #define CASE_RETURN_STR(const) case const: return #const; |
| |
| #define FNLOG() ALOGW(LOG_TAG, "%s", __FUNCTION__); |
| #define DEBUG(fmt, ...) ALOGD(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__) |
| #define INFO(fmt, ...) ALOGI(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__) |
| #define WARN(fmt, ...) ALOGW(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__) |
| #define ERROR(fmt, ...) ALOGE(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__) |
| |
| #define ASSERTC(cond, msg, val) if (!(cond)) {ERROR("### ASSERT : %s line %d %s (%d) ###", __FILE__, __LINE__, msg, val);} |
| |
| /***************************************************************************** |
| ** Local type definitions |
| ******************************************************************************/ |
| |
| struct a2dp_stream_common audio_stream; |
| bt_lib_callback_t *stack_cb = NULL; |
| static volatile unsigned char ack_recvd = 0; |
| pthread_cond_t ack_cond = PTHREAD_COND_INITIALIZER; |
| static int test = 0; |
| static bool update_initial_sink_latency = false; |
| int wait_for_stack_response(uint8_t time_to_wait); |
| bool resp_received = false; |
| static char a2dp_hal_imp[PROPERTY_VALUE_MAX] = "false"; |
| /***************************************************************************** |
| ** Static functions |
| ******************************************************************************/ |
| |
| audio_sbc_encoder_config_t sbc_codec; |
| audio_aptx_encoder_config_t aptx_codec; |
| audio_aptx_tws_encoder_config_t aptx_tws_codec; |
| audio_aac_encoder_config_t aac_codec; |
| audio_ldac_encoder_config_t ldac_codec; |
| audio_celt_encoder_config_t celt_codec; |
| /***************************************************************************** |
| ** Functions |
| ******************************************************************************/ |
| void a2dp_open_ctrl_path(struct a2dp_stream_common *common); |
| void ldac_codec_parser(uint8_t *codec_cfg); |
| /***************************************************************************** |
| ** Miscellaneous helper functions |
| ******************************************************************************/ |
| static const char* dump_a2dp_ctrl_event(char event) |
| { |
| switch(event) |
| { |
| CASE_RETURN_STR(A2DP_CTRL_CMD_NONE) |
| CASE_RETURN_STR(A2DP_CTRL_CMD_CHECK_READY) |
| CASE_RETURN_STR(A2DP_CTRL_CMD_START) |
| CASE_RETURN_STR(A2DP_CTRL_CMD_STOP) |
| CASE_RETURN_STR(A2DP_CTRL_CMD_SUSPEND) |
| CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_SUPPORTED) |
| CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) |
| CASE_RETURN_STR(A2DP_CTRL_CMD_CHECK_STREAM_STARTED) |
| CASE_RETURN_STR(A2DP_CTRL_GET_CODEC_CONFIG) |
| CASE_RETURN_STR(A2DP_CTRL_GET_MULTICAST_STATUS) |
| CASE_RETURN_STR(A2DP_CTRL_GET_CONNECTION_STATUS) |
| default: |
| return "UNKNOWN MSG ID"; |
| } |
| } |
| |
| static const char* dump_a2dp_ctrl_ack(tA2DP_CTRL_ACK resp) |
| { |
| switch(resp) |
| { |
| CASE_RETURN_STR(A2DP_CTRL_ACK_SUCCESS) |
| CASE_RETURN_STR(A2DP_CTRL_ACK_FAILURE) |
| CASE_RETURN_STR(A2DP_CTRL_ACK_INCALL_FAILURE) |
| CASE_RETURN_STR(A2DP_CTRL_ACK_UNSUPPORTED) |
| CASE_RETURN_STR(A2DP_CTRL_ACK_PENDING) |
| CASE_RETURN_STR(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS) |
| CASE_RETURN_STR(A2DP_CTRL_ACK_PREVIOUS_COMMAND_PENDING) |
| CASE_RETURN_STR(A2DP_CTRL_SKT_DISCONNECTED) |
| CASE_RETURN_STR(A2DP_CTRL_ACK_UNKNOWN) |
| default: |
| return "UNKNOWN ACK ID"; |
| } |
| } |
| |
| static const char* dump_a2dp_hal_state(int event) |
| { |
| switch(event) |
| { |
| CASE_RETURN_STR(AUDIO_A2DP_STATE_STARTING) |
| CASE_RETURN_STR(AUDIO_A2DP_STATE_STARTED) |
| CASE_RETURN_STR(AUDIO_A2DP_STATE_STOPPING) |
| CASE_RETURN_STR(AUDIO_A2DP_STATE_STOPPED) |
| CASE_RETURN_STR(AUDIO_A2DP_STATE_SUSPENDED) |
| CASE_RETURN_STR(AUDIO_A2DP_STATE_STANDBY) |
| default: |
| return "UNKNOWN STATE ID"; |
| } |
| } |
| static void* a2dp_codec_parser(uint8_t *codec_cfg, audio_format_t *codec_type, |
| uint32_t *sample_freq) |
| { |
| char byte,len; |
| uint8_t *p_cfg = codec_cfg; |
| ALOGW("%s",__func__); |
| ALOGW("%s: codec_type = %x",__func__, codec_cfg[CODEC_OFFSET]); |
| if (codec_cfg[CODEC_OFFSET] == CODEC_TYPE_PCM) |
| { |
| *codec_type = AUDIO_FORMAT_PCM_16_BIT; |
| //For the time being Audio does not require any param to be passed for PCM so returning null |
| return NULL; |
| } |
| else if (codec_cfg[CODEC_OFFSET] == CODEC_TYPE_SBC) |
| { |
| memset(&sbc_codec,0,sizeof(audio_sbc_encoder_config_t)); |
| p_cfg++;//skip dev idx |
| len = *p_cfg++; |
| p_cfg++;//skip media type |
| len--; |
| p_cfg++; |
| len--; |
| byte = *p_cfg++; |
| len--; |
| switch (byte & A2D_SBC_FREQ_MASK) |
| { |
| case A2D_SBC_SAMP_FREQ_48: |
| sbc_codec.sampling_rate = 48000; |
| break; |
| case A2D_SBC_SAMP_FREQ_44: |
| sbc_codec.sampling_rate = 44100; |
| break; |
| case A2D_SBC_SAMP_FREQ_32: |
| sbc_codec.sampling_rate = 3200; |
| break; |
| case A2D_SBC_SAMP_FREQ_16: |
| sbc_codec.sampling_rate = 16000; |
| break; |
| default: |
| ALOGE("SBC:Unkown sampling rate"); |
| } |
| |
| switch (byte & A2D_SBC_CHN_MASK) |
| { |
| case A2D_SBC_CH_MD_JOINT: |
| sbc_codec.channels = 3; |
| break; |
| case A2D_SBC_CH_MD_STEREO: |
| sbc_codec.channels = 2; |
| break; |
| case A2D_SBC_CH_MD_DUAL: |
| sbc_codec.channels = 1; |
| break; |
| case A2D_SBC_CH_MD_MONO: |
| sbc_codec.channels = 0; |
| break; |
| default: |
| ALOGE("SBC:Unknow channel mode"); |
| } |
| byte = *p_cfg++; |
| len--; |
| switch (byte & A2D_SBC_BLK_MASK) |
| { |
| case A2D_SBC_BLOCKS_16: |
| sbc_codec.blk_len = 16; |
| break; |
| case A2D_SBC_BLOCKS_12: |
| sbc_codec.blk_len = 12; |
| break; |
| case A2D_SBC_BLOCKS_8: |
| sbc_codec.blk_len = 8; |
| break; |
| case A2D_SBC_BLOCKS_4: |
| sbc_codec.blk_len = 4; |
| break; |
| default: |
| ALOGE("SBD:Unknown block length"); |
| } |
| |
| switch (byte & A2D_SBC_SUBBAND_MASK) |
| { |
| case A2D_SBC_SUBBAND_8: |
| sbc_codec.subband = 8; |
| break; |
| case A2D_SBC_SUBBAND_4: |
| sbc_codec.subband = 4; |
| break; |
| default: |
| ALOGE("SBD:Unknown subband"); |
| } |
| switch (byte & A2D_SBC_ALLOC_MASK) |
| { |
| case A2D_SBC_ALLOC_MD_L: |
| sbc_codec.alloc = 1; |
| break; |
| case A2D_SBC_ALLOC_MD_S: |
| sbc_codec.alloc = 2; |
| default: |
| ALOGE("SBD:Unknown alloc method"); |
| } |
| sbc_codec.min_bitpool = *p_cfg++; |
| len--; |
| sbc_codec.max_bitpool = *p_cfg++; |
| len--; |
| if (len == 0) |
| { |
| ALOGW("Copied codec config"); |
| } |
| p_cfg += 2; //skip mtu |
| sbc_codec.bitrate = *p_cfg++; |
| sbc_codec.bitrate |= (*p_cfg++ << 8); |
| sbc_codec.bitrate |= (*p_cfg++ << 16); |
| sbc_codec.bitrate |= (*p_cfg++ << 24); |
| *codec_type = AUDIO_FORMAT_SBC; |
| |
| if(sample_freq) *sample_freq = sbc_codec.sampling_rate; |
| |
| ALOGW("SBC: Done copying full codec config"); |
| return ((void *)(&sbc_codec)); |
| } else if (codec_cfg[CODEC_OFFSET] == CODEC_TYPE_AAC) |
| { |
| uint16_t aac_samp_freq = 0; |
| uint32_t aac_bit_rate = 0; |
| memset(&aac_codec,0,sizeof(audio_aac_encoder_config_t)); |
| p_cfg++;//skip dev idx |
| len = *p_cfg++; |
| p_cfg++;//skip media type |
| len--; |
| p_cfg++;//skip codec type |
| len--; |
| byte = *p_cfg++; |
| len--; |
| switch (byte & A2D_AAC_IE_OBJ_TYPE_MSK) |
| { |
| case A2D_AAC_IE_OBJ_TYPE_MPEG_2_AAC_LC: |
| aac_codec.enc_mode = AUDIO_FORMAT_AAC_SUB_LC; |
| break; |
| case A2D_AAC_IE_OBJ_TYPE_MPEG_4_AAC_LC: |
| aac_codec.enc_mode = AUDIO_FORMAT_AAC_SUB_LC; |
| break; |
| case A2D_AAC_IE_OBJ_TYPE_MPEG_4_AAC_LTP: |
| aac_codec.enc_mode = AUDIO_FORMAT_AAC_SUB_LTP; |
| break; |
| case A2D_AAC_IE_OBJ_TYPE_MPEG_4_AAC_SCA: |
| aac_codec.enc_mode = AUDIO_FORMAT_AAC_SUB_SCALABLE; |
| break; |
| default: |
| ALOGE("AAC:Unknown encoder mode"); |
| } |
| //USE 0 (AAC_LC) as hardcoded value till Audio |
| //define constants |
| aac_codec.enc_mode = 0; |
| //USE LOAS(1) or LATM(4) hardcoded values till |
| //Audio define proper constants |
| aac_codec.format_flag = 4; |
| byte = *p_cfg++; |
| len--; |
| aac_samp_freq = byte << 8; //1st byte of sample_freq |
| byte = *p_cfg++; |
| len--; |
| aac_samp_freq |= byte & 0x00F0; //1st nibble of second byte of samp_freq |
| |
| switch (aac_samp_freq) { |
| case 0x8000: aac_codec.sampling_rate = 8000; break; |
| case 0x4000: aac_codec.sampling_rate = 11025; break; |
| case 0x2000: aac_codec.sampling_rate = 12000; break; |
| case 0x1000: aac_codec.sampling_rate = 16000; break; |
| case 0x0800: aac_codec.sampling_rate = 22050; break; |
| case 0x0400: aac_codec.sampling_rate = 24000; break; |
| case 0x0200: aac_codec.sampling_rate = 32000; break; |
| case 0x0100: aac_codec.sampling_rate = 44100; break; |
| case 0x0080: aac_codec.sampling_rate = 48000; break; |
| case 0x0040: aac_codec.sampling_rate = 64000; break; |
| case 0x0020: aac_codec.sampling_rate = 88200; break; |
| case 0x0010: aac_codec.sampling_rate = 96000; break; |
| default: |
| ALOGE("Invalid sample_freq: %x", aac_samp_freq); |
| } |
| |
| switch (byte & A2D_AAC_IE_CHANNELS_MSK) |
| { |
| case A2D_AAC_IE_CHANNELS_1: |
| aac_codec.channels = 1; |
| break; |
| case A2D_AAC_IE_CHANNELS_2: |
| aac_codec.channels = 2; |
| break; |
| default: |
| ALOGE("AAC:Unknown channel mode"); |
| } |
| byte = *p_cfg++; //Move to VBR byte |
| len--; |
| switch (byte & A2D_AAC_IE_VBR_MSK) |
| { |
| case A2D_AAC_IE_VBR: |
| break; |
| default: |
| ALOGE("AAC:VBR not supported"); |
| } |
| aac_bit_rate = 0x7F&byte; |
| //Move it 2nd byte of 32 bit word. leaving the VBR bit |
| aac_bit_rate = aac_bit_rate << 16; |
| byte = *p_cfg++; //Move to 2nd byteof bitrate |
| len--; |
| |
| //Move it to 3rd byte of 32bit word |
| aac_bit_rate |= 0x0000FF00 & (((uint32_t)byte)<<8); |
| byte = *p_cfg++; //Move to 3rd byte of bitrate |
| len--; |
| |
| aac_bit_rate |= 0x000000FF & (((uint32_t)byte)); |
| aac_codec.bitrate = aac_bit_rate; |
| |
| *codec_type = AUDIO_FORMAT_AAC; |
| |
| if(sample_freq) *sample_freq = aac_codec.sampling_rate; |
| ALOGW("AAC: Done copying full codec config"); |
| return ((void *)(&aac_codec)); |
| } |
| else if (codec_cfg[CODEC_OFFSET] == NON_A2DP_CODEC_TYPE) |
| { |
| uint32_t vendor_ldac_id = 0x0; |
| vendor_ldac_id = (codec_cfg[VENDOR_ID_OFFSET] & 0x000000FF) | |
| ((codec_cfg[VENDOR_ID_OFFSET + 1]) << 8 & 0x0000FF00) | |
| ((codec_cfg[VENDOR_ID_OFFSET + 2]) << 16 & 0x00FF0000) | |
| ((codec_cfg[VENDOR_ID_OFFSET + 3]) << 24 & 0xFF000000); |
| |
| if (codec_cfg[VENDOR_ID_OFFSET] == VENDOR_APTX && |
| codec_cfg[CODEC_ID_OFFSET] == APTX_CODEC_ID) |
| { |
| ALOGW("AptX-classic codec"); |
| *codec_type = AUDIO_FORMAT_APTX; |
| } |
| if (codec_cfg[VENDOR_ID_OFFSET] == VENDOR_APTX_HD && |
| codec_cfg[CODEC_ID_OFFSET] == APTX_HD_CODEC_ID) |
| { |
| ALOGW("AptX-HD codec"); |
| *codec_type = AUDIO_FORMAT_APTX_HD; |
| } |
| |
| if (vendor_ldac_id == VENDOR_LDAC && |
| codec_cfg[CODEC_ID_OFFSET] == LDAC_CODEC_ID) |
| { |
| ALOGW("LDAC codec"); |
| *codec_type = AUDIO_FORMAT_LDAC; |
| ldac_codec_parser(codec_cfg); |
| if (sample_freq) *sample_freq = ldac_codec.sampling_rate; |
| return ((void *)&ldac_codec); |
| } |
| if (codec_cfg[VENDOR_ID_OFFSET] == VENDOR_APTX_HD && |
| codec_cfg[CODEC_ID_OFFSET] == APTX_TWS_CODEC_ID) |
| { |
| ALOGW("AptX-TWS codec"); |
| *codec_type = ENC_CODEC_TYPE_APTX_DUAL_MONO; |
| //aptx_codec.sync_mode = 0x01; |
| } |
| memset(&aptx_codec,0,sizeof(audio_aptx_encoder_config_t)); |
| p_cfg++; //skip dev_idx |
| len = *p_cfg++;//LOSC |
| p_cfg++; // Skip media type |
| len--; |
| p_cfg++; //codec_type |
| len--; |
| p_cfg+=4;//skip vendor id |
| len -= 4; |
| p_cfg += 2; //skip codec id |
| len -= 2; |
| byte = *p_cfg++; |
| len--; |
| switch (byte & A2D_APTX_SAMP_FREQ_MASK) |
| { |
| case A2D_APTX_SAMP_FREQ_48: |
| aptx_codec.sampling_rate = 48000; |
| break; |
| case A2D_APTX_SAMP_FREQ_44: |
| aptx_codec.sampling_rate = 44100; |
| break; |
| default: |
| ALOGE("Unknown sampling rate"); |
| } |
| switch (byte & A2D_APTX_CHAN_MASK) |
| { |
| case A2D_APTX_CHAN_STEREO: |
| case A2D_APTX_TWS_CHAN_MODE: |
| aptx_codec.channels = 2; |
| break; |
| case A2D_APTX_CHAN_MONO: |
| aptx_codec.channels = 1; |
| break; |
| default: |
| ALOGE("Unknown channel mode"); |
| } |
| if (*codec_type == AUDIO_FORMAT_APTX_HD) { |
| p_cfg += 4; |
| len -= 4;//ignore 4 bytes not used |
| } |
| if (len == 0) |
| { |
| ALOGW("Codec config copied"); |
| } |
| p_cfg += 2; //skip mtu |
| |
| aptx_codec.bitrate = *p_cfg++; |
| aptx_codec.bitrate |= (*p_cfg++ << 8); |
| aptx_codec.bitrate |= (*p_cfg++ << 16); |
| aptx_codec.bitrate |= (*p_cfg++ << 24); |
| |
| if(sample_freq) *sample_freq = aptx_codec.sampling_rate; |
| ALOGW("APTx: Done copying full codec config"); |
| if (*codec_type == ENC_CODEC_TYPE_APTX_DUAL_MONO) |
| { |
| memset(&aptx_tws_codec, 0, sizeof(audio_aptx_tws_encoder_config_t)); |
| memcpy(&aptx_tws_codec, &aptx_codec, sizeof(aptx_codec)); |
| aptx_tws_codec.sync_mode = 0x02; |
| return ((void *)&aptx_tws_codec); |
| } |
| return ((void *)&aptx_codec); |
| } |
| else if (codec_cfg[CODEC_OFFSET] == CODEC_TYPE_CELT) |
| { |
| uint8_t celt_samp_freq = 0; |
| uint32_t celt_bit_rate = 0; |
| memset(&celt_codec,0,sizeof(audio_celt_encoder_config_t)); |
| switch(codec_cfg[4] & A2D_CELT_SAMP_FREQ_MASK) |
| { |
| case A2D_CELT_SAMP_FREQ_48: |
| celt_codec.sampling_rate = 48000; |
| break; |
| case A2D_CELT_SAMP_FREQ_44: |
| celt_codec.sampling_rate = 44100; |
| break; |
| case A2D_CELT_SAMP_FREQ_32: |
| celt_codec.sampling_rate = 32000; |
| break; |
| default: |
| ALOGE("CELT: unknown sampl freq"); |
| } |
| switch(codec_cfg[4] & A2D_CELT_CHANNEL_MASK) |
| { |
| case A2D_CELT_CH_MONO: |
| celt_codec.channels = 1; |
| break; |
| case A2D_CELT_CH_STEREO: |
| celt_codec.channels = 2; |
| break; |
| default: |
| ALOGE("CELT: unknown channel"); |
| } |
| switch(codec_cfg[5] & A2D_CELT_FRAME_SIZE_MASK) |
| { |
| case A2D_CELT_FRAME_SIZE_64: |
| celt_codec.frame_size = 64; |
| break; |
| case A2D_CELT_FRAME_SIZE_128: |
| celt_codec.frame_size = 128; |
| break; |
| case A2D_CELT_FRAME_SIZE_256: |
| celt_codec.frame_size = 256; |
| break; |
| case A2D_CELT_FRAME_SIZE_512: |
| celt_codec.frame_size = 512; |
| break; |
| default: |
| ALOGE("CELT: unknown frame size"); |
| } |
| celt_codec.complexity = codec_cfg[5] & A2D_CELT_COMPLEXITY_MASK; |
| celt_codec.prediction_mode = |
| (codec_cfg[6] & A2D_CELT_PREDICTION_MODE_MASK) >> 4; |
| celt_codec.vbr_flag = codec_cfg[6] & A2D_CELT_VBR_MASK; |
| |
| celt_codec.bitrate |= codec_cfg[7]; |
| celt_codec.bitrate = celt_codec.bitrate << 8; |
| celt_codec.bitrate |= codec_cfg[8]; |
| celt_codec.bitrate = celt_codec.bitrate << 8; |
| celt_codec.bitrate |= codec_cfg[9]; |
| celt_codec.bitrate = celt_codec.bitrate << 8; |
| celt_codec.bitrate |= codec_cfg[10]; |
| *codec_type = AUDIO_CODEC_TYPE_CELT; |
| |
| ALOGE("CELT Bitrate: 0%x", celt_codec.bitrate); |
| ALOGE("CELT channel: 0%x", celt_codec.channels); |
| ALOGE("CELT complexity: 0%x", celt_codec.complexity); |
| ALOGE("CELT frame_size: 0%x", celt_codec.frame_size); |
| ALOGE("CELT prediction_mode: 0%x", celt_codec.prediction_mode); |
| ALOGE("CELT sampl_freq: 0%x", celt_codec.sampling_rate); |
| ALOGE("CELT vbr_flag: 0%x", celt_codec.vbr_flag); |
| ALOGE("CELT codec_type: 0%x", codec_type); |
| return ((void *)(&celt_codec)); |
| } |
| return NULL; |
| } |
| |
| int a2dp_read_codec_config(struct a2dp_stream_common *common,uint8_t idx) |
| { |
| char cmd[2];//,ack; |
| int i,len = 0; |
| uint8_t *p_codec_cfg = common->codec_cfg; |
| cmd[0] = A2DP_CTRL_GET_CODEC_CONFIG; |
| cmd[1] = idx; |
| ALOGW("%s",__func__); |
| memset(p_codec_cfg,0,MAX_CODEC_CFG_SIZE); |
| tA2DP_CTRL_ACK status = A2DP_CTRL_ACK_FAILURE; |
| |
| if(stack_cb) |
| { |
| ALOGW("Calling get_codec_cfg_cb"); |
| resp_received = false; |
| stack_cb->get_codec_cfg_cb(); |
| ack_recvd = 0; |
| if (resp_received == false) |
| { |
| ALOGW("%s: stack resp not received",__func__); |
| wait_for_stack_response(1); |
| } |
| status = common->ack_status; |
| common->ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| ALOGW("get_codec_cfg_cb returned: status = %s",dump_a2dp_ctrl_ack(status)); |
| } |
| return status; |
| } |
| |
| void a2dp_get_multicast_status(uint8_t *mcast_status) |
| { |
| ALOGW("%s",__func__); |
| if (stack_cb) |
| { |
| resp_received = false; |
| stack_cb->get_mcast_status_cb(); |
| ack_recvd = 0; |
| if (resp_received == false) |
| { |
| ALOGW("%s: stack resp not received",__func__); |
| wait_for_stack_response(1); |
| } |
| *mcast_status = audio_stream.multicast; |
| } |
| else |
| *mcast_status = 0; |
| } |
| |
| void a2dp_get_num_connected_devices(uint8_t *num_dev) |
| { |
| ALOGW("%s",__func__); |
| if (stack_cb) |
| { |
| resp_received = false; |
| stack_cb->get_connected_device_cb(); |
| ack_recvd = 0; |
| if (resp_received == false) |
| { |
| ALOGW("%s: stack resp not received",__func__); |
| wait_for_stack_response(1); |
| } |
| *num_dev = 1; |
| } |
| } |
| /***************************************************************************** |
| ** |
| ** AUDIO DATA PATH |
| ** |
| *****************************************************************************/ |
| |
| void a2dp_stream_common_init(struct a2dp_stream_common *common) |
| { |
| pthread_mutexattr_t lock_attr; |
| |
| //FNLOG(); |
| ALOGW("%s",__func__); |
| |
| pthread_mutexattr_init(&lock_attr); |
| pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE); |
| pthread_mutex_init(&common->lock, &lock_attr); |
| pthread_mutexattr_destroy(&lock_attr); |
| common->state = AUDIO_A2DP_STATE_STOPPED; |
| common->sink_latency = A2DP_DEFAULT_SINK_LATENCY; |
| |
| bt_split_a2dp_enabled = false; |
| } |
| |
| int wait_for_stack_response(uint8_t time_to_wait) |
| { |
| ALOGW("wait_for_stack_response"); |
| struct timespec now,wait_time; |
| uint8_t retry = 0; |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| if (stack_cb == NULL) |
| { |
| ALOGE("stack deinitialized"); |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| return retry; |
| } |
| // in race condition, ack_status is updated as SUCCESS |
| // without ack_recvd made 0. |
| if (audio_stream.ack_status == A2DP_CTRL_ACK_SUCCESS) |
| { |
| ALOGE("ACK Success, no need to wait"); |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| return retry; |
| } |
| while (retry < CTRL_CHAN_RETRY_COUNT && |
| ack_recvd == 0) |
| { |
| ALOGW("entering coditional wait: retry = %d, ack_recvd = %d",retry,ack_recvd); |
| clock_gettime(CLOCK_REALTIME, &now); |
| now.tv_sec += time_to_wait; |
| pthread_cond_timedwait(&ack_cond, &audio_stream.ack_lock, &now); |
| retry++; |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| if (ack_recvd) { |
| ALOGV("wait_for_stack_response: ack received"); |
| } |
| ALOGV("wait_for_stack_response returning retry = %d",retry); |
| return retry; |
| } |
| static void copy_status(tA2DP_CTRL_ACK status) |
| { |
| ALOGW("copy_status: status = %d",status); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| audio_stream.ack_status = status; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| void bt_stack_init(bt_lib_callback_t *lib_cb) |
| { |
| ALOGW("bt_stack_init"); |
| int ret = 0; |
| stack_cb = lib_cb; |
| } |
| void bt_stack_deinit(tA2DP_CTRL_ACK status) |
| { |
| ALOGW("bt_stack_deinit"); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| stack_cb = NULL; |
| audio_stream.ack_status = status; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| |
| void bt_stack_on_stream_started(tA2DP_CTRL_ACK status) |
| { |
| ALOGW("bt_stack_on_stream_started: status = %d",status); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| if ((audio_stream.ack_status != A2DP_CTRL_ACK_UNKNOWN) && (status == A2DP_CTRL_ACK_PENDING)) { |
| ALOGW("status already changed to = %d, don't update pending",audio_stream.ack_status); |
| } |
| else { |
| audio_stream.ack_status = status; |
| } |
| resp_received = true; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| |
| void bt_stack_on_stream_suspended(tA2DP_CTRL_ACK status) |
| { |
| ALOGW("bt_stack_on_stream_suspended status = %d, ack_status = %d ", status, audio_stream.ack_status); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| if ((audio_stream.ack_status != A2DP_CTRL_ACK_UNKNOWN) && (status == A2DP_CTRL_ACK_PENDING)) { |
| ALOGW("status already changed to = %d, don't update pending",audio_stream.ack_status); |
| } |
| else { |
| audio_stream.ack_status = status; |
| ALOGW("bt_stack_on_stream_suspended updating ack_status = %d ", audio_stream.ack_status); |
| } |
| resp_received = true; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| ALOGW("bt_stack_on_stream_suspended signalling pthread "); |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| ALOGW("bt_stack_on_stream_suspended mutex unlocked "); |
| } |
| |
| void bt_stack_on_stream_stopped(tA2DP_CTRL_ACK status) |
| { |
| ALOGW("bt_stack_on_stream_stopped"); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| if ((audio_stream.ack_status != A2DP_CTRL_ACK_UNKNOWN) && (status == A2DP_CTRL_ACK_PENDING)) { |
| ALOGW("status already changed to = %d, don't update pending",audio_stream.ack_status); |
| } |
| else { |
| audio_stream.ack_status = status; |
| } |
| resp_received = true; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| |
| void bt_stack_on_get_codec_cfg(tA2DP_CTRL_ACK status, const char *p_cfg, |
| size_t len) |
| { |
| ALOGW("bt_stack_on_get_codec_config status %s",dump_a2dp_ctrl_ack(status)); |
| if (status != A2DP_CTRL_ACK_PENDING) |
| { |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| audio_stream.ack_status = status; |
| ALOGW("bt_stack_on_get_codec_config len = %d",len); |
| if (len > MAX_CODEC_CFG_SIZE) { |
| ALOGE("codec config length > MAX_CODEC_CFG_SIZE"); |
| status = A2DP_CTRL_ACK_FAILURE; |
| } |
| if (status == A2DP_CTRL_ACK_SUCCESS) |
| { |
| memcpy(audio_stream.codec_cfg,p_cfg,len); |
| for (int i = 0; i < len; i++) { |
| ALOGV("audio_stream.codec_cfg[%d] = %x",i,audio_stream.codec_cfg[i]); |
| } |
| } |
| resp_received = true; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| else |
| { |
| ALOGW("bt_stack_on_get_codec_cfg status pending"); |
| } |
| } |
| |
| void bt_stack_on_get_mcast_status(uint8_t status) |
| { |
| ALOGW("bt_stack_on_get_mcast_status"); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| audio_stream.multicast = status; |
| resp_received = true; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| |
| void bt_stack_on_get_num_connected_devices(uint8_t num_dev) |
| { |
| ALOGW("bt_stack_on_get_num_connected_devices"); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| audio_stream.num_conn_dev = num_dev; |
| resp_received = true; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| |
| void bt_stack_on_get_connection_status(tA2DP_CTRL_ACK status) |
| { |
| ALOGW("bt_stack_on_get_connection_status"); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| audio_stream.ack_status = status; |
| resp_received = true; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| void bt_stack_on_check_a2dp_ready(tA2DP_CTRL_ACK status) |
| { |
| ALOGW("bt_stack_on_check_a2dp_ready"); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| audio_stream.ack_status = status; |
| resp_received = true; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| |
| void bt_stack_on_get_sink_latency(uint16_t latency) |
| { |
| ALOGW("bt_stack_on_get_sink_latency"); |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| audio_stream.sink_latency = latency; |
| resp_received = true; |
| if (!ack_recvd) |
| { |
| ack_recvd = 1; |
| pthread_cond_signal(&ack_cond); |
| } |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } |
| |
| int audio_start_stream() |
| { |
| int i, j, ack_ret; |
| tA2DP_CTRL_ACK status = A2DP_CTRL_ACK_SUCCESS; |
| ALOGW("%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state)); |
| pthread_mutex_lock(&audio_stream.lock); |
| if (audio_stream.state == AUDIO_A2DP_STATE_SUSPENDED) |
| { |
| ALOGW("stream suspended"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return -1; |
| } |
| if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") && |
| !strcmp(a2dp_hal_imp, "true")) |
| { |
| if (audio_stream.state == AUDIO_A2DP_STATE_STARTED) |
| { |
| INFO("stream already started"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return 0; |
| } |
| } |
| for (j = 0; j <STREAM_START_MAX_RETRY_LOOPER; j++) { |
| for (i = 0; i < STREAM_START_MAX_RETRY_COUNT; i++) |
| { |
| if (stack_cb) |
| { |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| resp_received = false; |
| stack_cb->start_req_cb(); |
| ack_recvd = 0; |
| if (!resp_received) |
| { |
| ack_ret = wait_for_stack_response(1); |
| if (ack_ret == CTRL_CHAN_RETRY_COUNT && !ack_recvd) |
| { |
| ALOGE("audio_start_stream: Failed to get ack from stack"); |
| status = -1; |
| goto end; |
| } |
| } |
| status = audio_stream.ack_status; |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| ALOGW("audio_start_stream status = %s",dump_a2dp_ctrl_ack(status)); |
| if (status == A2DP_CTRL_ACK_PENDING) |
| { |
| ALOGW("waiting in pending"); |
| ack_recvd = 0; |
| if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") && |
| !strcmp(a2dp_hal_imp, "true")) |
| { |
| wait_for_stack_response(1); |
| if (audio_stream.ack_status == A2DP_CTRL_ACK_UNKNOWN) |
| { |
| ALOGW("audio_start_stream ack not received, fake as success"); |
| status = A2DP_CTRL_ACK_SUCCESS; |
| } |
| else |
| { |
| status = audio_stream.ack_status; |
| } |
| } |
| else |
| { |
| wait_for_stack_response(5); |
| status = audio_stream.ack_status; |
| } |
| ALOGW("done waiting in pending status = %s",dump_a2dp_ctrl_ack(status)); |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| } |
| |
| if (status == A2DP_CTRL_ACK_SUCCESS) |
| { |
| ALOGW("a2dp stream started successfully"); |
| audio_stream.state = AUDIO_A2DP_STATE_STARTED; |
| goto end; |
| } |
| else if (status == A2DP_CTRL_ACK_INCALL_FAILURE || |
| status == A2DP_CTRL_ACK_UNSUPPORTED || |
| status == A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS || |
| status == A2DP_CTRL_ACK_UNKNOWN) |
| { |
| ALOGW("a2dp stream start failed: status = %s",dump_a2dp_ctrl_ack(status)); |
| audio_stream.state = AUDIO_A2DP_STATE_STOPPED; |
| goto end; |
| } |
| else if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") && |
| !strcmp(a2dp_hal_imp, "true") && |
| status == A2DP_CTRL_ACK_PREVIOUS_COMMAND_PENDING) |
| { |
| ALOGW("a2dp stream start exited as prev command is pending, fake as success"); |
| audio_stream.state = AUDIO_A2DP_STATE_STARTED; |
| goto end; |
| } |
| else if (status == A2DP_CTRL_ACK_FAILURE) |
| { |
| ALOGW("a2dp stream start failed: generic failure"); |
| } |
| } |
| else |
| { |
| ALOGW("%s:Stack shutdown",__func__); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return A2DP_CTRL_SKT_DISCONNECTED; |
| } |
| ALOGW("%s: a2dp stream not started,wait 100mse & retry", __func__); |
| usleep(100000); |
| } |
| ALOGW("%s: Check if valid connection is still up or not", __func__); |
| |
| // For every 1 sec check if a2dp is still up, to avoid |
| // blocking the audio thread forever if a2dp connection is closed |
| // for some reason |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| resp_received = false; |
| stack_cb->get_connection_status_cb(); |
| ack_recvd = 0; |
| if (!resp_received) |
| { |
| ack_ret = wait_for_stack_response(1); |
| if (ack_ret == CTRL_CHAN_RETRY_COUNT && !ack_recvd) |
| { |
| ALOGE("audio_start_stream: Failed to get ack from stack"); |
| status = -1; |
| goto end; |
| } |
| } |
| status = audio_stream.ack_status; |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| if (status != A2DP_CTRL_ACK_SUCCESS) |
| { |
| ALOGE("%s: No valid a2dp connection\n", __func__); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return -1; |
| } |
| } |
| end: |
| if (audio_stream.state != AUDIO_A2DP_STATE_STARTED) |
| { |
| ALOGE("%s: Failed to start a2dp stream", __func__); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return status; |
| } |
| pthread_mutex_unlock(&audio_stream.lock); |
| INFO("stream successfully started"); |
| return status; |
| } |
| |
| int audio_stream_open() |
| { |
| ALOGW("%s",__func__); |
| a2dp_stream_common_init(&audio_stream); |
| bt_split_a2dp_enabled = true; |
| if (stack_cb != NULL) |
| { |
| ALOGW("audio_stream_open: Success"); |
| return 0; |
| } |
| ALOGW("audio_stream_open: Failed"); |
| return -1; |
| } |
| |
| int audio_stream_close() |
| { |
| ALOGW("%s",__func__); |
| tA2DP_CTRL_ACK status = A2DP_CTRL_ACK_SUCCESS; |
| pthread_mutex_lock(&audio_stream.lock); |
| if (audio_stream.state == AUDIO_A2DP_STATE_STARTED || |
| audio_stream.state == AUDIO_A2DP_STATE_STOPPING) |
| { |
| ALOGW("%s: Suspending audio stream",__func__); |
| if (stack_cb) |
| { |
| int ack_ret = 0; |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| resp_received = false; |
| stack_cb->suspend_req_cb(); |
| ack_recvd = 0; |
| if (!resp_received) |
| { |
| ack_ret = wait_for_stack_response(1); |
| if (ack_ret == 3 && |
| audio_stream.ack_status == A2DP_CTRL_ACK_UNKNOWN) |
| { |
| ALOGE("audio_stream_close: Failed to get ack from stack"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return -1; |
| } |
| } |
| } |
| } |
| pthread_mutex_unlock(&audio_stream.lock); |
| return 0; |
| } |
| int audio_stop_stream() |
| { |
| ALOGW("%s",__func__); |
| int ret = -1; |
| tA2DP_CTRL_ACK status; |
| pthread_mutex_lock(&audio_stream.lock); |
| if (stack_cb) |
| { |
| if (audio_stream.state != AUDIO_A2DP_STATE_SUSPENDED) |
| { |
| int ack_ret = 0; |
| ack_recvd = 0; |
| resp_received = false; |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| stack_cb->suspend_req_cb(); |
| if (!resp_received) |
| { |
| ack_ret = wait_for_stack_response(1); |
| if (ack_ret == CTRL_CHAN_RETRY_COUNT && !ack_recvd) |
| { |
| ALOGE("audio_stop_stream: Failed to get ack from stack"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return -1; |
| } |
| } |
| status = audio_stream.ack_status; |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| ALOGW("audio_stop_stream: ack status = %s",dump_a2dp_ctrl_ack(status)); |
| if (status == A2DP_CTRL_ACK_PENDING) |
| { |
| ack_recvd = 0; |
| if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") && |
| !strcmp(a2dp_hal_imp, "true")) |
| { |
| wait_for_stack_response(1); |
| } |
| else |
| { |
| wait_for_stack_response(5); |
| } |
| status = audio_stream.ack_status; |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| if (status == A2DP_CTRL_ACK_SUCCESS) ret = 0; |
| } |
| else if (status == A2DP_CTRL_ACK_SUCCESS) |
| { |
| ALOGW("audio stop stream successful"); |
| audio_stream.state = AUDIO_A2DP_STATE_STANDBY; |
| pthread_mutex_unlock(&audio_stream.lock); |
| return 0; |
| } |
| else if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") && |
| !strcmp(a2dp_hal_imp, "true") && |
| status == A2DP_CTRL_ACK_PREVIOUS_COMMAND_PENDING) |
| { |
| ALOGW("a2dp stream stop exited as prev command is pending, fake as success"); |
| audio_stream.state = AUDIO_A2DP_STATE_STANDBY; |
| pthread_mutex_unlock(&audio_stream.lock); |
| return 0; |
| } |
| else |
| { |
| ALOGW("audio stop stream failed"); |
| audio_stream.state = AUDIO_A2DP_STATE_STOPPED; |
| pthread_mutex_unlock(&audio_stream.lock); |
| return -1; |
| } |
| } |
| } |
| else |
| ALOGW("stack is down"); |
| audio_stream.state = AUDIO_A2DP_STATE_STOPPED; |
| pthread_mutex_unlock(&audio_stream.lock); |
| return ret; |
| } |
| |
| int audio_suspend_stream() |
| { |
| ALOGW("%s",__func__); |
| tA2DP_CTRL_ACK status; |
| |
| pthread_mutex_lock(&audio_stream.lock); |
| if (stack_cb) |
| { |
| if (audio_stream.state != AUDIO_A2DP_STATE_SUSPENDED) |
| { |
| int ack_ret = 0; |
| ack_recvd = 0; |
| resp_received = false; |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| stack_cb->suspend_req_cb(); |
| if (!resp_received) |
| { |
| ack_ret = wait_for_stack_response(1); |
| if (ack_ret == CTRL_CHAN_RETRY_COUNT && !ack_recvd) |
| { |
| ALOGE("audio_suspend_stream: Failed to get ack from stack"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return -1; |
| } |
| } |
| status = audio_stream.ack_status; |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| ALOGW("audio_suspend_stream: ack status = %s",dump_a2dp_ctrl_ack(status)); |
| if (status == A2DP_CTRL_ACK_PENDING) |
| { |
| //TODO wait for the response; |
| ack_recvd = 0; |
| if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") && |
| !strcmp(a2dp_hal_imp, "true")) |
| { |
| wait_for_stack_response(1); |
| } |
| else |
| { |
| wait_for_stack_response(5); |
| } |
| status = audio_stream.ack_status; |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| } |
| else if (status == A2DP_CTRL_ACK_SUCCESS) |
| { |
| ALOGW("audio suspend stream successful"); |
| audio_stream.state = AUDIO_A2DP_STATE_SUSPENDED; |
| pthread_mutex_unlock(&audio_stream.lock); |
| return 0; |
| } |
| else if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") && |
| !strcmp(a2dp_hal_imp, "true") && |
| status == A2DP_CTRL_ACK_PREVIOUS_COMMAND_PENDING) |
| { |
| ALOGW("a2dp stream suspend exited as prev command is pending, fake as success"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| audio_stream.state = AUDIO_A2DP_STATE_SUSPENDED; |
| return 0; |
| } |
| else |
| { |
| ALOGW("audio suspend stream failed"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return -1; |
| } |
| } |
| } |
| else |
| ALOGW("stack is down"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return -1; |
| } |
| |
| void audio_handoff_triggered() |
| { |
| ALOGW("%s state = %s",__func__,dump_a2dp_hal_state(audio_stream.state)); |
| pthread_mutex_lock(&audio_stream.lock); |
| if (audio_stream.state != AUDIO_A2DP_STATE_STOPPED || |
| audio_stream.state != AUDIO_A2DP_STATE_STOPPING) |
| { |
| audio_stream.state = AUDIO_A2DP_STATE_STOPPED; |
| } |
| pthread_mutex_unlock(&audio_stream.lock); |
| } |
| |
| void clear_a2dpsuspend_flag() |
| { |
| ALOGW("%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state)); |
| pthread_mutex_lock(&audio_stream.lock); |
| if (audio_stream.state == AUDIO_A2DP_STATE_SUSPENDED) |
| audio_stream.state = AUDIO_A2DP_STATE_STOPPED; |
| pthread_mutex_unlock(&audio_stream.lock); |
| } |
| |
| void * audio_get_codec_config(uint8_t *multicast_status, uint8_t *num_dev, |
| audio_format_t *codec_type) |
| { |
| int i, status; |
| ALOGW("%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state)); |
| |
| pthread_mutex_lock(&audio_stream.lock); |
| a2dp_get_multicast_status(multicast_status); |
| if (*multicast_status) |
| { |
| a2dp_get_num_connected_devices(num_dev); |
| } |
| else |
| *num_dev = 1; |
| ALOGW("got multicast status = %d dev = %d",*multicast_status,*num_dev); |
| update_initial_sink_latency = true; |
| |
| for (i = 0; i < STREAM_START_MAX_RETRY_COUNT; i++) |
| { |
| status = a2dp_read_codec_config(&audio_stream, 0); |
| if (status == A2DP_CTRL_ACK_SUCCESS) |
| { |
| pthread_mutex_unlock(&audio_stream.lock); |
| if (stack_cb == NULL) { |
| ALOGW("get codec config returned due to stack deinit"); |
| return NULL; |
| } |
| return (a2dp_codec_parser(&audio_stream.codec_cfg[0], codec_type, NULL)); |
| } |
| INFO("%s: a2dp stream not configured,wait 100mse & retry", __func__); |
| usleep(100000); |
| } |
| pthread_mutex_unlock(&audio_stream.lock); |
| return NULL; |
| } |
| |
| void* audio_get_next_codec_config(uint8_t idx, audio_format_t *codec_type) |
| { |
| int i, status; |
| ALOGW("%s",__func__); |
| pthread_mutex_lock(&audio_stream.lock); |
| for (i = 0; i < STREAM_START_MAX_RETRY_COUNT; i++) |
| { |
| status = a2dp_read_codec_config(&audio_stream,idx); |
| if (status == A2DP_CTRL_ACK_SUCCESS) |
| { |
| pthread_mutex_unlock(&audio_stream.lock); |
| return (a2dp_codec_parser(&audio_stream.codec_cfg[0], codec_type, NULL)); |
| } |
| INFO("%s: a2dp stream not configured,wait 100mse & retry", __func__); |
| usleep(100000); |
| } |
| pthread_mutex_unlock(&audio_stream.lock); |
| return NULL; |
| } |
| |
| int audio_check_a2dp_ready() |
| { |
| int i, ack_ret; |
| ALOGW("audio_check_a2dp_ready: state %s", dump_a2dp_hal_state(audio_stream.state)); |
| tA2DP_CTRL_ACK status; |
| pthread_mutex_lock(&audio_stream.lock); |
| if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") && |
| !strcmp(a2dp_hal_imp, "true") && |
| audio_stream.state == AUDIO_A2DP_STATE_SUSPENDED) |
| { |
| INFO("stream not ready to start"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return 0; |
| } |
| |
| for (i = 0; i < CHECK_A2DP_READY_MAX_COUNT; i++) |
| { |
| pthread_mutex_lock(&audio_stream.ack_lock); |
| if (stack_cb != NULL) { |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| ack_recvd = 0; |
| stack_cb->a2dp_check_ready_cb(); |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| } else { |
| ALOGW("audio_check_a2dp_ready = NOT ready - callbacks not registered"); |
| pthread_mutex_unlock(&audio_stream.ack_lock); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return 0; |
| } |
| |
| ack_ret = wait_for_stack_response(1); |
| status = audio_stream.ack_status; |
| if (status == A2DP_CTRL_ACK_SUCCESS) |
| { |
| ALOGW("audio_check_a2dp_ready : %s",dump_a2dp_ctrl_ack(status)); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return 1; |
| } |
| if (ack_ret == CTRL_CHAN_RETRY_COUNT && !ack_recvd) |
| { |
| ALOGE("audio_check_a2dp_ready: Failed to get ack from stack"); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return 0; |
| } |
| ALOGW("audio_check_a2dp_ready(): a2dp stream not ready, wait 200msec & retry"); |
| usleep(200000); |
| } |
| audio_stream.ack_status = A2DP_CTRL_ACK_UNKNOWN; |
| ALOGW("audio_check_a2dp_ready = %s",dump_a2dp_ctrl_ack(status)); |
| |
| pthread_mutex_unlock(&audio_stream.lock); |
| return status == A2DP_CTRL_ACK_SUCCESS; |
| } |
| |
| uint16_t audio_get_a2dp_sink_latency() |
| { |
| ALOGD_IF(DEBUG, "%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state)); |
| pthread_mutex_lock(&audio_stream.lock); |
| if (update_initial_sink_latency) |
| { |
| if (stack_cb) |
| { |
| resp_received = false; |
| stack_cb->get_sink_latency_cb(); |
| ack_recvd = 0; |
| if (resp_received == false) |
| wait_for_stack_response(1); |
| } |
| else |
| audio_stream.sink_latency = A2DP_DEFAULT_SINK_LATENCY; |
| update_initial_sink_latency = false; |
| } |
| pthread_mutex_unlock(&audio_stream.lock); |
| return audio_stream.sink_latency; |
| } |
| |
| bool audio_is_scrambling_enabled(void) |
| { |
| audio_format_t codec_type = AUDIO_FORMAT_DEFAULT; |
| int i; |
| char value[PROPERTY_VALUE_MAX]; |
| uint8_t *codec_cfg = NULL; |
| uint32_t sample_freq = 0; |
| memset(value, '\0', sizeof(char)*PROPERTY_VALUE_MAX); |
| ALOGW("audio_is_scrambling_enabled: state %s", |
| dump_a2dp_hal_state(audio_stream.state)); |
| tA2DP_CTRL_ACK status = A2DP_CTRL_ACK_UNKNOWN; |
| |
| if (stack_cb == NULL) |
| { |
| ALOGW("audio_is_scrambling_enabled returned false due to stack deinit"); |
| return false; |
| } |
| |
| if( property_get("persist.vendor.bluetooth.soc.scram_freqs", value, "false") && |
| !strcmp(value, "false")) |
| { |
| property_get("persist.vendor.bt.soc.scram_freqs", value, "false"); |
| if(!strcmp(value, "false")) |
| { |
| ALOGW("persist.vendor.bt.soc.scram_freqs is not set"); |
| return false; |
| } |
| } |
| else |
| { |
| ALOGE("Error in fetching persist.vendor.bluetooth.soc.scram_freqs property"); |
| return false; |
| } |
| |
| pthread_mutex_lock(&audio_stream.lock); |
| for (i = 0; i < STREAM_START_MAX_RETRY_COUNT; i++) |
| { |
| status = a2dp_read_codec_config(&audio_stream, 0); |
| if (status == A2DP_CTRL_ACK_SUCCESS) |
| { |
| if(!a2dp_codec_parser(&audio_stream.codec_cfg[0], |
| &codec_type, &sample_freq)) { |
| status = A2DP_CTRL_ACK_UNKNOWN; |
| } |
| break; |
| } |
| INFO("%s: a2dp stream not configured,wait 100mse & retry", __func__); |
| usleep(100000); |
| } |
| if (codec_type == ENC_CODEC_TYPE_APTX_DUAL_MONO) { |
| INFO("%s:TWSP codec, return false",__func__); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return false; |
| } |
| if(status == A2DP_CTRL_ACK_SUCCESS) { |
| |
| if (codec_type == CODEC_TYPE_CELT) { |
| INFO("%s: BA going on,return false", __func__); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return false; |
| } |
| |
| ALOGW("audio_is_scrambling_enabled sample_freq %ld",sample_freq); |
| switch (sample_freq) { |
| case 44100: |
| if(!strstr(value, "441")) status = A2DP_CTRL_ACK_UNKNOWN; |
| break; |
| case 48000: |
| if(!strstr(value, "48")) status = A2DP_CTRL_ACK_UNKNOWN; |
| break; |
| case 88200: |
| if(!strstr(value, "882")) status = A2DP_CTRL_ACK_UNKNOWN; |
| break; |
| case 96000: |
| if(!strstr(value, "96")) status = A2DP_CTRL_ACK_UNKNOWN; |
| break; |
| case 176400: |
| if(!strstr(value, "1764")) status = A2DP_CTRL_ACK_UNKNOWN; |
| break; |
| case 192000: |
| if(!strstr(value, "192")) status = A2DP_CTRL_ACK_UNKNOWN; |
| break; |
| default: |
| ALOGE("Invalid sampling freqency, return A2DP_CTRL_ACK_UNKNOWN"); |
| status = A2DP_CTRL_ACK_UNKNOWN; |
| break; |
| } |
| } |
| ALOGW("audio_is_scrambling_enabled = %s",dump_a2dp_ctrl_ack(status)); |
| pthread_mutex_unlock(&audio_stream.lock); |
| return status == A2DP_CTRL_ACK_SUCCESS; |
| } |
| |
| void ldac_codec_parser(uint8_t *codec_cfg) |
| { |
| char byte,len; |
| uint8_t *p_cfg = codec_cfg; |
| memset(&ldac_codec,0,sizeof(audio_ldac_encoder_config_t)); |
| p_cfg++; //skip dev_idx |
| len = *p_cfg++;//LOSC |
| p_cfg++; // Skip media type |
| len--; |
| p_cfg++; //codec_type |
| len--; |
| p_cfg+=4;//skip vendor id |
| len -= 4; |
| p_cfg += 2; //skip codec id |
| len -= 2; |
| byte = *p_cfg++; |
| len--; |
| switch (byte & A2D_LDAC_SAMP_FREQ_MASK) |
| { |
| case A2D_LDAC_SAMP_FREQ_44: |
| ldac_codec.sampling_rate = 44100; |
| break; |
| case A2D_LDAC_SAMP_FREQ_48: |
| ldac_codec.sampling_rate = 48000; |
| break; |
| case A2D_LDAC_SAMP_FREQ_88: |
| ldac_codec.sampling_rate = 88200; |
| break; |
| case A2D_LDAC_SAMP_FREQ_96: |
| ldac_codec.sampling_rate = 96000; |
| break; |
| case A2D_LDAC_SAMP_FREQ_176: |
| ldac_codec.sampling_rate = 176400; |
| break; |
| case A2D_LDAC_SAMP_FREQ_192: |
| ldac_codec.sampling_rate = 192000; |
| break; |
| default: |
| ALOGE("Unknown sampling rate"); |
| } |
| ALOGW("%s: LDAC: sample rate: %lu", __func__, ldac_codec.sampling_rate); |
| byte = *p_cfg++; |
| len--; |
| ldac_codec.channel_mode = (byte & A2D_LDAC_CHAN_MASK); |
| if (len == 0) |
| { |
| ALOGW("Codec config copied"); |
| } |
| ldac_codec.mtu = DEFAULT_MTU_SIZE; |
| p_cfg += 2; |
| |
| ldac_codec.bitrate = *p_cfg++; |
| ldac_codec.bitrate |= (*p_cfg++ << 8); |
| ldac_codec.bitrate |= (*p_cfg++ << 16); |
| ldac_codec.bitrate |= (*p_cfg++ << 24); |
| |
| ldac_codec.is_abr_enabled = (ldac_codec.bitrate == 0); |
| |
| ALOGW("Create Lookup for %d with ABR %d", ldac_codec.sampling_rate, ldac_codec.is_abr_enabled); |
| if (ldac_codec.sampling_rate == 44100 || |
| ldac_codec.sampling_rate == 88200) { |
| int num_of_level_entries = |
| sizeof(bit_rate_level_44_1k_88_2k_database)/sizeof(bit_rate_level_44_1k_88_2k_table_t); |
| ldac_codec.level_to_bitrate_map.num_levels = num_of_level_entries; |
| if (ldac_codec.is_abr_enabled) { |
| ldac_codec.bitrate = bit_rate_level_44_1k_88_2k_database[0].bit_rate_value; |
| ALOGW("Send start highest bit-rate value %d", ldac_codec.bitrate); |
| } |
| for (int i = 0; i < num_of_level_entries; i++) { |
| ldac_codec.level_to_bitrate_map.bit_rate_level_map[i].link_quality_level = |
| bit_rate_level_44_1k_88_2k_database[i].level_value; |
| ldac_codec.level_to_bitrate_map.bit_rate_level_map[i].bitrate = |
| bit_rate_level_44_1k_88_2k_database[i].bit_rate_value; |
| ALOGW("Level: %d, bit-rate: %d", |
| ldac_codec.level_to_bitrate_map.bit_rate_level_map[i].link_quality_level, |
| ldac_codec.level_to_bitrate_map.bit_rate_level_map[i].bitrate); |
| } |
| } else if (ldac_codec.sampling_rate == 48000 || |
| ldac_codec.sampling_rate == 96000) { |
| int num_of_level_entries = |
| sizeof(bit_rate_level_48k_96k_database)/sizeof(bit_rate_level_48k_96k_table_t); |
| ldac_codec.level_to_bitrate_map.num_levels = num_of_level_entries; |
| if (ldac_codec.is_abr_enabled) { |
| ldac_codec.bitrate = bit_rate_level_48k_96k_database[0].bit_rate_value; |
| ALOGW("Send start highest bit-rate value %d", ldac_codec.bitrate); |
| } |
| for (int i = 0; i < num_of_level_entries; i++) { |
| ldac_codec.level_to_bitrate_map.bit_rate_level_map[i].link_quality_level = |
| bit_rate_level_48k_96k_database[i].level_value; |
| ldac_codec.level_to_bitrate_map.bit_rate_level_map[i].bitrate = |
| bit_rate_level_48k_96k_database[i].bit_rate_value; |
| ALOGW("Level: %d, bit-rate: %d", |
| ldac_codec.level_to_bitrate_map.bit_rate_level_map[i].link_quality_level, |
| ldac_codec.level_to_bitrate_map.bit_rate_level_map[i].bitrate); |
| } |
| } else { |
| ALOGW("Unsupported Invalid frequency"); |
| } |
| ALOGW("%s: LDAC: bitrate: %lu", __func__, ldac_codec.bitrate); |
| ALOGW("LDAC: Done copying full codec config"); |
| } |