/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted 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.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_TAG "split_a2dp"
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
#include <errno.h>
#include <cutils/log.h>
#include "audio_hw.h"
#include "platform.h"
#include "platform_api.h"
#include <stdlib.h>
#include <cutils/str_parms.h>
#include <hardware/audio.h>
#include <hardware/hardware.h>
#ifdef SPLIT_A2DP_ENABLED
struct a2dp_data{
struct audio_stream_out *a2dp_stream;
struct audio_hw_device *a2dp_device;
bool a2dp_started;
bool a2dp_suspended;
};
struct a2dp_data a2dp;
#define AUDIO_PARAMETER_A2DP_STARTED "A2dpStarted"
static int open_a2dp_output()
{
hw_module_t *mod;
int format = AUDIO_FORMAT_PCM_16_BIT;
int rc=0;
uint32_t channels = AUDIO_CHANNEL_OUT_STEREO;
uint32_t sampleRate = DEFAULT_OUTPUT_SAMPLING_RATE;
struct audio_config config;
ALOGV("open_a2dp_output");
config.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
config.format = AUDIO_FORMAT_PCM_16_BIT;
if (a2dp.a2dp_device == NULL){
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, (const char*)"a2dp",
(const hw_module_t**)&mod);
if (rc != 0) {
ALOGE("Could not get a2dp hardware module");
return rc;
}
ALOGV("Opening A2DP device HAL for the first time");
rc = audio_hw_device_open(mod, &a2dp.a2dp_device);
if (rc != 0) {
ALOGE("couldn't open a2dp audio hw device");
return rc;
}
}
rc = a2dp.a2dp_device->open_output_stream(a2dp.a2dp_device, 0,AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
(audio_output_flags_t)AUDIO_OUTPUT_FLAG_NONE, &config, &a2dp.a2dp_stream, NULL);
if( rc != 0 ) {
ALOGE("Failed to open output stream for a2dp: status %d", rc);
}
a2dp.a2dp_suspended = false;
return rc;
}
static int close_a2dp_output()
{
ALOGV("close_a2dp_output");
if(!a2dp.a2dp_device && !a2dp.a2dp_stream){
ALOGE("No Active A2dp output found");
return 0;
}
a2dp.a2dp_device->close_output_stream(a2dp.a2dp_device, a2dp.a2dp_stream);
a2dp.a2dp_stream = NULL;
a2dp.a2dp_started = false;
a2dp.a2dp_suspended = true;
return 0;
}
void audio_extn_a2dp_set_parameters(struct str_parms *parms)
{
int ret, val;
char value[32]={0};
ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value,
sizeof(value));
if( ret >= 0) {
val = atoi(value);
if (val & AUDIO_DEVICE_OUT_ALL_A2DP) {
ALOGV("Received device connect request for A2DP");
open_a2dp_output();
}
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value,
sizeof(value));
if( ret >= 0) {
val = atoi(value);
if (val & AUDIO_DEVICE_OUT_ALL_A2DP) {
ALOGV("Received device dis- connect request");
close_a2dp_output();
}
}
ret = str_parms_get_str(parms, "A2dpSuspended", value, sizeof(value));
if (ret >= 0) {
if (a2dp.a2dp_device && a2dp.a2dp_stream) {
a2dp.a2dp_device->set_parameters(a2dp.a2dp_device, str_parms_to_str(parms));
if (!strncmp(value,"true",sizeof(value))) {
a2dp.a2dp_suspended = true;
} else {
a2dp.a2dp_suspended = false;
}
}
}
}
void audio_extn_a2dp_start_playback()
{
int ret = 0;
char buf[20]={0};
if (!a2dp.a2dp_started && a2dp.a2dp_device && a2dp.a2dp_stream) {
snprintf(buf,sizeof(buf),"%s=true",AUDIO_PARAMETER_A2DP_STARTED);
/* This call indicates BT HAL to start playback */
ret = a2dp.a2dp_device->set_parameters(a2dp.a2dp_device, buf);
if (ret < 0 ) {
ALOGE("BT controller start failed, retry on the next write");
a2dp.a2dp_started = false;
} else {
a2dp.a2dp_started = true;
ALOGV("Start playback successful to BT HAL");
}
}
}
void audio_extn_a2dp_stop_playback()
{
int ret =0;
char buf[20]={0};
if ( a2dp.a2dp_started && a2dp.a2dp_device && a2dp.a2dp_stream) {
snprintf(buf,sizeof(buf),"%s=false",AUDIO_PARAMETER_A2DP_STARTED);
ret = a2dp.a2dp_device->set_parameters(a2dp.a2dp_device, buf);
if (ret < 0)
ALOGE("out_standby to BT HAL failed");
else
ALOGV("out_standby to BT HAL successful");
}
a2dp.a2dp_started = false;
a2dp.a2dp_suspended = true;
}
void audio_extn_a2dp_init ()
{
a2dp.a2dp_started = false;
a2dp.a2dp_suspended = true;
a2dp.a2dp_stream = NULL;
a2dp.a2dp_device = NULL;
}
#endif // SPLIT_A2DP_ENABLED