From e842363710c949cf9d44a1d9959de711817a9993 Mon Sep 17 00:00:00 2001 From: pramod kotreshappa Date: Thu, 31 Mar 2016 14:34:20 -0700 Subject: [PATCH] A2DP: New BT IPC library New BT IPC library that facilitates communication betwee audio hal and BT host. Bthost-ipc library will be loaded by audio primary hal in Split A2dp scenario to communicate with bt host and in legacy non-split a2dp scenario this library will be loaded by a2dp hal. Change-Id: Ie11d88b27662c97a00ac9d484ae9c621293949e8 --- audio_a2dp_hw/Android.mk | 22 + audio_a2dp_hw/audio_a2dp_hw.c | 221 ++++++++-- audio_a2dp_hw/audio_a2dp_hw.h | 35 +- audio_a2dp_hw/bthost_ipc.c | 997 ++++++++++++++++++++++++++++++++++++++++++ audio_a2dp_hw/bthost_ipc.h | 155 +++++++ 5 files changed, 1383 insertions(+), 47 deletions(-) create mode 100644 audio_a2dp_hw/bthost_ipc.c create mode 100644 audio_a2dp_hw/bthost_ipc.h diff --git a/audio_a2dp_hw/Android.mk b/audio_a2dp_hw/Android.mk index 0dc138820..7581068ef 100644 --- a/audio_a2dp_hw/Android.mk +++ b/audio_a2dp_hw/Android.mk @@ -12,6 +12,7 @@ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/../ \ $(LOCAL_PATH)/../utils/include +LOCAL_CFLAGS = -DBT_HOST_IPC_ENABLED LOCAL_MODULE := audio.a2dp.default LOCAL_MODULE_RELATIVE_PATH := hw @@ -25,3 +26,24 @@ LOCAL_CONLYFLAGS += $(bluetooth_CONLYFLAGS) LOCAL_CPPFLAGS += $(bluetooth_CPPFLAGS) include $(BUILD_SHARED_LIBRARY) + +########################################## +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := bthost_ipc.c + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/bthost_ipc.h \ + $(LOCAL_PATH)/../ \ + $(LOCAL_PATH)/../utils/include + +LOCAL_CFLAGS = $(bdroid_CFLAGS) + +LOCAL_MODULE := libbthost_if +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_STATIC_LIBRARIES := libosi + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/audio_a2dp_hw/audio_a2dp_hw.c b/audio_a2dp_hw/audio_a2dp_hw.c index f9b6e0e97..37baeb847 100644 --- a/audio_a2dp_hw/audio_a2dp_hw.c +++ b/audio_a2dp_hw/audio_a2dp_hw.c @@ -1,5 +1,9 @@ /****************************************************************************** + * Copyright (C) 2016, The Linux Foundation. All rights reserved. * + * Not a Contribution + ******************************************************************************/ +/***************************************************************************** * Copyright (C) 2009-2012 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -56,6 +60,12 @@ #include "osi/include/osi.h" #include "osi/include/socket_utils/sockets.h" +#include + +#ifdef BT_HOST_IPC_ENABLED +#include "bthost_ipc.h" +#endif + #ifdef BT_AUDIO_SYSTRACE_LOG #include #endif @@ -88,20 +98,10 @@ static int number =0; #define ERROR(fmt, ...) LOG_ERROR(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);} - +//#define BT_HOST_IPC_PATH "/system/lib/hw/bthost-ipc.so" /***************************************************************************** ** Local type definitions ******************************************************************************/ - -typedef enum { - AUDIO_A2DP_STATE_STARTING, - AUDIO_A2DP_STATE_STARTED, - AUDIO_A2DP_STATE_STOPPING, - AUDIO_A2DP_STATE_STOPPED, - AUDIO_A2DP_STATE_SUSPENDED, /* need explicit set param call to resume (suspend=false) */ - AUDIO_A2DP_STATE_STANDBY /* allows write to autoresume */ -} a2dp_state_t; - struct a2dp_stream_in; struct a2dp_stream_out; @@ -111,23 +111,6 @@ struct a2dp_audio_device { struct a2dp_stream_out *output; }; -struct a2dp_config { - uint32_t rate; - uint32_t channel_flags; - int format; -}; - -/* move ctrl_fd outside output stream and keep open until HAL unloaded ? */ - -struct a2dp_stream_common { - pthread_mutex_t lock; - int ctrl_fd; - int audio_fd; - size_t buffer_sz; - struct a2dp_config cfg; - a2dp_state_t state; -}; - struct a2dp_stream_out { struct audio_stream_out stream; struct a2dp_stream_common common; @@ -143,7 +126,10 @@ struct a2dp_stream_in { /***************************************************************************** ** Static variables ******************************************************************************/ - +#ifdef BT_HOST_IPC_ENABLED +static void *lib_handle = NULL; +bt_host_ipc_interface_t *ipc_if = NULL; +#endif /***************************************************************************** ** Static functions ******************************************************************************/ @@ -159,8 +145,9 @@ static size_t out_get_buffer_size(const struct audio_stream *stream); ******************************************************************************/ /* Function used only in debug mode */ static const char* dump_a2dp_ctrl_event(char event) __attribute__ ((unused)); +#ifndef BT_HOST_IPC_ENABLED static void a2dp_open_ctrl_path(struct a2dp_stream_common *common); - +#endif /***************************************************************************** ** Miscellaneous helper functions ******************************************************************************/ @@ -175,6 +162,8 @@ static const char* dump_a2dp_ctrl_event(char event) CASE_RETURN_STR(A2DP_CTRL_CMD_STOP) CASE_RETURN_STR(A2DP_CTRL_CMD_SUSPEND) CASE_RETURN_STR(A2DP_CTRL_CMD_CHECK_STREAM_STARTED) + CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_SUPPORTED) + CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) default: return "UNKNOWN MSG ID"; } @@ -209,6 +198,7 @@ static void ts_error_log(char *tag, int val, int buff_size, struct a2dp_config c } } +#ifndef BT_HOST_IPC_ENABLED /* logs timestamp with microsec precision pprev is optional in case a dedicated diff is required */ static void ts_log(char *tag, int val, struct timespec *pprev_opt) @@ -253,6 +243,8 @@ static const char* dump_a2dp_hal_state(int event) return "UNKNOWN STATE ID"; } } + +//#ifndef BT_HOST_IPC_ENABLED /***************************************************************************** ** ** bluedroid stack adaptation @@ -597,7 +589,6 @@ static int start_audio_datapath(struct a2dp_stream_common *common) #else common->state = AUDIO_A2DP_STATE_STARTED; #endif - return 0; error: @@ -668,7 +659,7 @@ static int check_a2dp_stream_started(struct a2dp_stream_out *out) } return 0; } - +#endif /***************************************************************************** ** @@ -699,7 +690,11 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, if ((out->common.state == AUDIO_A2DP_STATE_STOPPED) || (out->common.state == AUDIO_A2DP_STATE_STANDBY)) { +#ifdef BT_HOST_IPC_ENABLED + if (ipc_if->start_audio_datapath(&out->common) < 0) +#else if (start_audio_datapath(&out->common) < 0) +#endif { goto error; } @@ -728,7 +723,11 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, } #endif +#ifdef BT_HOST_IPC_ENABLED + sent = ipc_if->skt_write(out->common.audio_fd, buffer, bytes); +#else sent = skt_write(out->common.audio_fd, buffer, bytes); +#endif pthread_mutex_lock(&out->common.lock); #ifdef BT_AUDIO_SYSTRACE_LOG @@ -738,8 +737,13 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, } #endif - if (sent == -1) { + if (sent == -1) + { +#ifdef BT_HOST_IPC_ENABLED + ipc_if->skt_disconnect(out->common.audio_fd); +#else skt_disconnect(out->common.audio_fd); +#endif out->common.audio_fd = AUDIO_SKT_DISCONNECTED; if ((out->common.state != AUDIO_A2DP_STATE_SUSPENDED) && (out->common.state != AUDIO_A2DP_STATE_STOPPING)) { @@ -842,7 +846,11 @@ static int out_standby(struct audio_stream *stream) pthread_mutex_lock(&out->common.lock); // Do nothing in SUSPENDED state. if (out->common.state != AUDIO_A2DP_STATE_SUSPENDED) - retVal = suspend_audio_datapath(&out->common, true); +#ifdef BT_HOST_IPC_ENABLED + retVal = ipc_if->suspend_audio_datapath(&out->common, true); +#else + retVal = suspend_audio_datapath(&out->common, true); +#endif out->frames_rendered = 0; // rendered is reset, presented is not pthread_mutex_unlock (&out->common.lock); @@ -890,7 +898,11 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) else if ((out->common.state == AUDIO_A2DP_STATE_STOPPED) || (out->common.state == AUDIO_A2DP_STATE_STANDBY)) { +#ifdef BT_HOST_IPC_ENABLED + if (ipc_if->start_audio_datapath(&out->common) < 0) +#else if (start_audio_datapath(&out->common) < 0) +#endif { INFO("stream start failed"); status = -1; @@ -909,7 +921,11 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) INFO("out_set_parameters, value: false"); pthread_mutex_lock(&out->common.lock); if (out->common.state != AUDIO_A2DP_STATE_SUSPENDED) +#ifdef BT_HOST_IPC_ENABLED + status = ipc_if->suspend_audio_datapath(&out->common, true); +#else status = suspend_audio_datapath(&out->common, true); +#endif else { ERROR("stream alreday suspended"); @@ -936,16 +952,28 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) { pthread_mutex_lock(&out->common.lock); if (out->common.state == AUDIO_A2DP_STATE_STARTED) +#ifdef BT_HOST_IPC_ENABLED + status = ipc_if->suspend_audio_datapath(&out->common, false); +#else status = suspend_audio_datapath(&out->common, false); +#endif else { - if (check_a2dp_stream_started(out) == 0) - /*Btif and A2dp HAL state can be out of sync - *check state of btif and suspend audio. - *Happens when remote initiates start.*/ - status = suspend_audio_datapath(&out->common, false); - else - out->common.state = AUDIO_A2DP_STATE_SUSPENDED; +#ifdef BT_HOST_IPC_ENABLED + if (ipc_if->check_a2dp_stream_started(&out->common) == 0) +#else + if (check_a2dp_stream_started(out) == 0) +#endif + /*Btif and A2dp HAL state can be out of sync + *check state of btif and suspend audio. + *Happens when remote initiates start.*/ +#ifdef BT_HOST_IPC_ENABLED + status = ipc_if->suspend_audio_datapath(&out->common, false); +#else + status = suspend_audio_datapath(&out->common, false); +#endif + else + out->common.state = AUDIO_A2DP_STATE_SUSPENDED; } pthread_mutex_unlock(&out->common.lock); } @@ -1192,7 +1220,11 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, if ((in->common.state == AUDIO_A2DP_STATE_STOPPED) || (in->common.state == AUDIO_A2DP_STATE_STANDBY)) { +#ifdef BT_HOST_IPC_ENABLED + if (ipc_if->start_audio_datapath(&in->common) < 0) +#else if (start_audio_datapath(&in->common) < 0) +#endif { goto error; } @@ -1204,11 +1236,20 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, } pthread_mutex_unlock(&in->common.lock); +#ifdef BT_HOST_IPC_ENABLED + read = ipc_if->skt_read(in->common.audio_fd, buffer, bytes); +#else read = skt_read(in->common.audio_fd, buffer, bytes); +#endif pthread_mutex_lock(&in->common.lock); + if (read == -1) { +#ifdef BT_HOST_IPC_ENABLED + ipc_if->skt_disconnect(in->common.audio_fd); +#else skt_disconnect(in->common.audio_fd); +#endif in->common.audio_fd = AUDIO_SKT_DISCONNECTED; if ((in->common.state != AUDIO_A2DP_STATE_SUSPENDED) && (in->common.state != AUDIO_A2DP_STATE_STOPPING)) { @@ -1293,6 +1334,25 @@ static int adev_open_output_stream(struct audio_hw_device *dev, number++; #endif +#ifdef BT_HOST_IPC_ENABLED + lib_handle = dlopen("libbthost_if.so", RTLD_NOW); + if (!lib_handle) + { + INFO("Failed to load bthost-ipc library %s",dlerror()); + ret = -1; + goto err_open; + } + else + { + ipc_if = (bt_host_ipc_interface_t*) dlsym(lib_handle,"BTHOST_IPC_INTERFACE"); + if (!ipc_if) + { + ERROR("Failed to load BT IPC library symbol"); + ret = -1; + goto err_open; + } + } +#endif out->stream.common.get_sample_rate = out_get_sample_rate; out->stream.common.set_sample_rate = out_set_sample_rate; out->stream.common.get_buffer_size = out_get_buffer_size; @@ -1313,8 +1373,11 @@ static int adev_open_output_stream(struct audio_hw_device *dev, /* initialize a2dp specifics */ +#ifdef BT_HOST_IPC_ENABLED + ipc_if->a2dp_stream_common_init(&out->common); +#else a2dp_stream_common_init(&out->common); - +#endif out->common.cfg.channel_flags = AUDIO_STREAM_DEFAULT_CHANNEL_FLAG; out->common.cfg.format = AUDIO_STREAM_DEFAULT_FORMAT; out->common.cfg.rate = AUDIO_STREAM_DEFAULT_RATE; @@ -1329,7 +1392,11 @@ static int adev_open_output_stream(struct audio_hw_device *dev, *stream_out = &out->stream; a2dp_dev->output = out; +#ifdef BT_HOST_IPC_ENABLED + ipc_if->a2dp_open_ctrl_path(&out->common); +#else a2dp_open_ctrl_path(&out->common); +#endif if (out->common.ctrl_fd == AUDIO_SKT_DISCONNECTED) { ERROR("ctrl socket failed to connect (%s)", strerror(errno)); @@ -1337,6 +1404,13 @@ static int adev_open_output_stream(struct audio_hw_device *dev, goto err_open; } +#ifdef BT_HOST_IPC_ENABLED + if (ipc_if->a2dp_command(&out->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) { +#else + if (a2dp_command(&out->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) { +#endif + DEBUG("Streaming mode set successfully"); + } INFO("success"); /* Delay to ensure Headset is in proper state when START is initiated from DUT immediately after the connection due to ongoing music playback. */ @@ -1361,17 +1435,26 @@ static void adev_close_output_stream(struct audio_hw_device *dev, pthread_mutex_lock(&out->common.lock); if ((out->common.state == AUDIO_A2DP_STATE_STARTED) || - (out->common.state == AUDIO_A2DP_STATE_STOPPING)) { + (out->common.state == AUDIO_A2DP_STATE_STOPPING)) +#ifdef BT_HOST_IPC_ENABLED + ipc_if->stop_audio_datapath(&out->common); +#else stop_audio_datapath(&out->common); - } +#endif #ifdef BT_AUDIO_SAMPLE_LOG ALOGV("close file output"); fclose (outputpcmsamplefile); #endif +#ifdef BT_HOST_IPC_ENABLED + ipc_if->skt_disconnect(out->common.ctrl_fd); +#else skt_disconnect(out->common.ctrl_fd); +#endif out->common.ctrl_fd = AUDIO_SKT_DISCONNECTED; + if (lib_handle) + dlclose(lib_handle); free(stream); a2dp_dev->output = NULL; pthread_mutex_unlock(&out->common.lock); @@ -1504,6 +1587,25 @@ static int adev_open_input_stream(struct audio_hw_device *dev, if (!in) return -ENOMEM; +#ifdef BT_HOST_IPC_ENABLED + lib_handle = dlopen("libbthost_if.so", RTLD_NOW); + if (!lib_handle) + { + INFO("Failed to load bthost-ipc library %s",dlerror()); + ret = -1; + goto err_open; + } + else + { + ipc_if = (bt_host_ipc_interface_t*) dlsym(lib_handle,"BTHOST_IPC_INTERFACE"); + if (!ipc_if) + { + ERROR("Failed to load BT IPC library symbol"); + ret = -1; + goto err_open; + } + } +#endif in->stream.common.get_sample_rate = in_get_sample_rate; in->stream.common.set_sample_rate = in_set_sample_rate; in->stream.common.get_buffer_size = in_get_buffer_size; @@ -1521,12 +1623,19 @@ static int adev_open_input_stream(struct audio_hw_device *dev, in->stream.get_input_frames_lost = in_get_input_frames_lost; /* initialize a2dp specifics */ +#ifdef BT_HOST_IPC_ENABLED + ipc_if->a2dp_stream_common_init(&in->common); +#else a2dp_stream_common_init(&in->common); - +#endif *stream_in = &in->stream; a2dp_dev->input = in; +#ifdef BT_HOST_IPC_ENABLED + ipc_if->a2dp_open_ctrl_path(&in->common); +#else a2dp_open_ctrl_path(&in->common); +#endif if (in->common.ctrl_fd == AUDIO_SKT_DISCONNECTED) { ERROR("ctrl socket failed to connect (%s)", strerror(errno)); @@ -1534,7 +1643,18 @@ static int adev_open_input_stream(struct audio_hw_device *dev, goto err_open; } +#ifdef BT_HOST_IPC_ENABLED + if (ipc_if->a2dp_command(&in->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) { +#else + if (a2dp_command(&in->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) { +#endif + DEBUG("Streaming mode set successfully"); + } +#ifdef BT_HOST_IPC_ENABLED + if (ipc_if->a2dp_read_audio_config(&in->common) < 0) { +#else if (a2dp_read_audio_config(&in->common) < 0) { +#endif ERROR("a2dp_read_audio_config failed (%s)", strerror(errno)); ret = -1; goto err_open; @@ -1561,13 +1681,22 @@ static void adev_close_input_stream(struct audio_hw_device *dev, INFO("closing input (state %d)", state); if ((state == AUDIO_A2DP_STATE_STARTED) || (state == AUDIO_A2DP_STATE_STOPPING)) +#ifdef BT_HOST_IPC_ENABLED + ipc_if->stop_audio_datapath(&in->common); +#else stop_audio_datapath(&in->common); +#endif +#ifdef BT_HOST_IPC_ENABLED + ipc_if->skt_disconnect(in->common.ctrl_fd); +#else skt_disconnect(in->common.ctrl_fd); +#endif in->common.ctrl_fd = AUDIO_SKT_DISCONNECTED; free(stream); a2dp_dev->input = NULL; - + if (lib_handle) + dlclose(lib_handle); DEBUG("done"); } diff --git a/audio_a2dp_hw/audio_a2dp_hw.h b/audio_a2dp_hw/audio_a2dp_hw.h index 1a639e946..8c30b76e0 100644 --- a/audio_a2dp_hw/audio_a2dp_hw.h +++ b/audio_a2dp_hw/audio_a2dp_hw.h @@ -1,4 +1,9 @@ /****************************************************************************** + * Copyright (C) 2016, The Linux Foundation. All rights reserved. + * + * Not a Contribution + *****************************************************************************/ +/****************************************************************************** * * Copyright (C) 2009-2012 Broadcom Corporation * @@ -26,7 +31,7 @@ #ifndef AUDIO_A2DP_HW_H #define AUDIO_A2DP_HW_H - +#include /***************************************************************************** ** Constants & Macros ******************************************************************************/ @@ -91,6 +96,8 @@ typedef enum { A2DP_CTRL_CMD_SUSPEND, A2DP_CTRL_GET_AUDIO_CONFIG, A2DP_CTRL_CMD_OFFLOAD_START, + A2DP_CTRL_CMD_OFFLOAD_SUPPORTED, + A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED, } tA2DP_CTRL_CMD; typedef enum { @@ -101,6 +108,32 @@ typedef enum { } tA2DP_CTRL_ACK; +typedef enum { + AUDIO_A2DP_STATE_STARTING, + AUDIO_A2DP_STATE_STARTED, + AUDIO_A2DP_STATE_STOPPING, + AUDIO_A2DP_STATE_STOPPED, + AUDIO_A2DP_STATE_SUSPENDED, /* need explicit set param call to resume (suspend=false) */ + AUDIO_A2DP_STATE_STANDBY /* allows write to autoresume */ +} a2dp_state_t; + +struct a2dp_config { + uint32_t rate; + uint32_t channel_flags; + int format; +}; + +/* move ctrl_fd outside output stream and keep open until HAL unloaded ? */ + +struct a2dp_stream_common { + pthread_mutex_t lock; + int ctrl_fd; + int audio_fd; + size_t buffer_sz; + struct a2dp_config cfg; + a2dp_state_t state; + uint8_t codec_cfg[20]; +}; /***************************************************************************** ** Type definitions for callback functions ******************************************************************************/ diff --git a/audio_a2dp_hw/bthost_ipc.c b/audio_a2dp_hw/bthost_ipc.c new file mode 100644 index 000000000..ef2073325 --- /dev/null +++ b/audio_a2dp_hw/bthost_ipc.c @@ -0,0 +1,997 @@ +/****************************************************************************** + * Copyright (C) 2016, The Linux Foundation. All rights reserved. + * + * Not a Contribution + *****************************************************************************/ +/***************************************************************************** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "bthost_ipc.h" +#include "bt_utils.h" +#include "osi/include/hash_map.h" +#include "osi/include/hash_map_utils.h" +#include "osi/include/log.h" +#include "osi/include/osi.h" +#include "osi/include/socket_utils/sockets.h" + + +#define LOG_TAG "bthost_ipc" +#include "osi/include/log.h" + + +static int bt_split_a2dp_enabled = 0; +/***************************************************************************** +** Constants & Macros +******************************************************************************/ +#define STREAM_START_MAX_RETRY_COUNT 50 +#define CTRL_CHAN_RETRY_COUNT 3 +#define USEC_PER_SEC 1000000L +#define SOCK_SEND_TIMEOUT_MS 2000 /* Timeout for sending */ +#define SOCK_RECV_TIMEOUT_MS 5000 /* Timeout for receiving */ + +// set WRITE_POLL_MS to 0 for blocking sockets, nonzero for polled non-blocking sockets +#define WRITE_POLL_MS 20 + +#define CASE_RETURN_STR(const) case const: return #const; + +#define FNLOG() LOG_VERBOSE(LOG_TAG, "%s", __FUNCTION__); +#define DEBUG(fmt, ...) LOG_VERBOSE(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__) +#define INFO(fmt, ...) LOG_INFO(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__) +#define WARN(fmt, ...) LOG_WARN(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__) +#define ERROR(fmt, ...) LOG_ERROR(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; + +/***************************************************************************** +** Static functions +******************************************************************************/ + +audio_sbc_encoder_config sbc_codec; +audio_aptx_encoder_config aptx_codec; +/***************************************************************************** +** Externs +******************************************************************************/ + +/***************************************************************************** +** Functions +******************************************************************************/ +void a2dp_open_ctrl_path(struct a2dp_stream_common *common); +/***************************************************************************** +** 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) + default: + return "UNKNOWN MSG ID"; + } +} + +/* logs timestamp with microsec precision + pprev is optional in case a dedicated diff is required */ +static void ts_log(char *tag, int val, struct timespec *pprev_opt) +{ + struct timespec now; + static struct timespec prev = {0,0}; + unsigned long long now_us; + unsigned long long diff_us; + UNUSED(tag); + UNUSED(val); + + clock_gettime(CLOCK_MONOTONIC, &now); + + now_us = now.tv_sec*USEC_PER_SEC + now.tv_nsec/1000; + + if (pprev_opt) + { + diff_us = (now.tv_sec - prev.tv_sec) * USEC_PER_SEC + (now.tv_nsec - prev.tv_nsec)/1000; + *pprev_opt = now; + DEBUG("[%s] ts %08lld, *diff %08lld, val %d", tag, now_us, diff_us, val); + } + else + { + diff_us = (now.tv_sec - prev.tv_sec) * USEC_PER_SEC + (now.tv_nsec - prev.tv_nsec)/1000; + prev = now; + DEBUG("[%s] ts %08lld, diff %08lld, val %d", tag, now_us, diff_us, val); + } +} + + +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) +{ + char byte,len; + uint8_t *p_cfg = codec_cfg; + INFO("%s",__func__); + if (codec_cfg[CODEC_OFFSET] == CODEC_TYPE_SBC) + { + memset(&sbc_codec,0,sizeof(audio_sbc_encoder_config)); + 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: + ERROR("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: + ERROR("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: + ERROR("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: + ERROR("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: + ERROR("Unknown alloc method"); + } + sbc_codec.min_bitpool = *p_cfg++; + len--; + sbc_codec.max_bitpool = *p_cfg++; + len--; + if (len == 0) + INFO("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; + INFO("Done copying full codec config"); + return ((void *)(&sbc_codec)); + } + else if (codec_cfg[CODEC_OFFSET] == NON_A2DP_CODEC_TYPE) + { + if (codec_cfg[VENDOR_ID_OFFSET] == VENDOR_APTX && + codec_cfg[CODEC_ID_OFFSET] == APTX_CODEC_ID) + { + INFO("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) + { + INFO("AptX-HD codec"); + *codec_type = AUDIO_FORMAT_APTX_HD; + } + memset(&aptx_codec,0,sizeof(audio_aptx_encoder_config)); + 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: + ERROR("Unknown sampling rate"); + } + switch (byte & A2D_APTX_CHAN_MASK) + { + case A2D_APTX_CHAN_STEREO: + aptx_codec.channels = 2; + break; + case A2D_APTX_CHAN_MONO: + aptx_codec.channels = 1; + break; + default: + ERROR("Unknown channel mode"); + } + if (*codec_type == AUDIO_FORMAT_APTX_HD) + len -= 4;//ignore 4 bytes not used + if (len == 0) + INFO("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); + return ((void *)&aptx_codec); + } + return NULL; +} +/***************************************************************************** +** +** bluedroid stack adaptation +** +*****************************************************************************/ + +static int skt_connect(char *path, size_t buffer_sz) +{ + int ret; + int skt_fd; + int len; + + INFO("connect to %s (sz %zu)", path, buffer_sz); + + skt_fd = socket(AF_LOCAL, SOCK_STREAM, 0); + + if(osi_socket_local_client_connect(skt_fd, path, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM) < 0) + { + ERROR("failed to connect (%s)", strerror(errno)); + close(skt_fd); + return -1; + } + + len = buffer_sz; + ret = setsockopt(skt_fd, SOL_SOCKET, SO_SNDBUF, (char*)&len, (int)sizeof(len)); + if (ret < 0) + ERROR("setsockopt failed (%s)", strerror(errno)); + + ret = setsockopt(skt_fd, SOL_SOCKET, SO_RCVBUF, (char*)&len, (int)sizeof(len)); + if (ret < 0) + ERROR("setsockopt failed (%s)", strerror(errno)); + + /* Socket send/receive timeout value */ + struct timeval tv; + tv.tv_sec = SOCK_SEND_TIMEOUT_MS / 1000; + tv.tv_usec = (SOCK_SEND_TIMEOUT_MS % 1000) * 1000; + + ret = setsockopt(skt_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + if (ret < 0) + ERROR("setsockopt failed (%s)", strerror(errno)); + + tv.tv_sec = SOCK_RECV_TIMEOUT_MS / 1000; + tv.tv_usec = (SOCK_RECV_TIMEOUT_MS % 1000) * 1000; + + ret = setsockopt(skt_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + if (ret < 0) + ERROR("setsockopt failed (%s)", strerror(errno)); + + INFO("connected to stack fd = %d", skt_fd); + + return skt_fd; +} + +static int skt_read(int fd, void *p, size_t len) +{ + ssize_t read; + + FNLOG(); + + ts_log("skt_read recv", len, NULL); + + OSI_NO_INTR(read = recv(fd, p, len, MSG_NOSIGNAL)); + if (read == -1) + ERROR("read failed with errno=%d\n", errno); + + return (int)read; +} + +static int skt_write(int fd, const void *p, size_t len) +{ + ssize_t sent; + + FNLOG(); + + ts_log("skt_write", len, NULL); + + if (WRITE_POLL_MS == 0) { + // do not poll, use blocking send + OSI_NO_INTR(sent = send(fd, p, len, MSG_NOSIGNAL)); + if (sent == -1) + ERROR("write failed with error(%s)", strerror(errno)); + + return (int)sent; + } + + // use non-blocking send, poll + int ms_timeout = SOCK_SEND_TIMEOUT_MS; + size_t count = 0; + while (count < len) { + OSI_NO_INTR(sent = send(fd, p, len - count, MSG_NOSIGNAL | MSG_DONTWAIT)); + if (sent == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + ERROR("write failed with error(%s)", strerror(errno)); + return -1; + } + if (ms_timeout >= WRITE_POLL_MS) { + usleep(WRITE_POLL_MS * 1000); + ms_timeout -= WRITE_POLL_MS; + continue; + } + WARN("write timeout exceeded, sent %zu bytes", count); + return -1; + } + count += sent; + p = (const uint8_t *)p + sent; + } + return (int)count; +} + +static int skt_disconnect(int fd) +{ + INFO("fd %d", fd); + + if (fd != AUDIO_SKT_DISCONNECTED) + { + shutdown(fd, SHUT_RDWR); + close(fd); + } + return 0; +} + + + +/***************************************************************************** +** +** AUDIO CONTROL PATH +** +*****************************************************************************/ + +int a2dp_ctrl_receive(struct a2dp_stream_common *common, void* buffer, int length) +{ + ssize_t ret; + int i; + + for (i = 0;; i++) { + OSI_NO_INTR(ret = recv(common->ctrl_fd, buffer, length, MSG_NOSIGNAL)); + if (ret > 0) { + break; + } + if (ret == 0) { + ERROR("ack failed: peer closed"); + break; + } + if (errno != EWOULDBLOCK && errno != EAGAIN) { + ERROR("ack failed: error(%s)", strerror(errno)); + break; + } + if (i == (CTRL_CHAN_RETRY_COUNT - 1)) { + ERROR("ack failed: max retry count"); + break; + } + INFO("ack failed (%s), retrying", strerror(errno)); + } + if (ret <= 0) { + skt_disconnect(common->ctrl_fd); + common->ctrl_fd = AUDIO_SKT_DISCONNECTED; + } + return ret; +} + +int a2dp_command(struct a2dp_stream_common *common, char cmd) +{ + char ack; + + INFO("A2DP COMMAND %s", dump_a2dp_ctrl_event(cmd)); + + if (common->ctrl_fd == AUDIO_SKT_DISCONNECTED) { + INFO("recovering from previous error"); + a2dp_open_ctrl_path(common); + if (common->ctrl_fd == AUDIO_SKT_DISCONNECTED) { + ERROR("failure to open ctrl path"); + return -1; + } + } + + /* send command */ + ssize_t sent; + OSI_NO_INTR(sent = send(common->ctrl_fd, &cmd, 1, MSG_NOSIGNAL)); + if (sent == -1) + { + ERROR("cmd failed (%s)", strerror(errno)); + skt_disconnect(common->ctrl_fd); + common->ctrl_fd = AUDIO_SKT_DISCONNECTED; + return -1; + } + + /* wait for ack byte */ + if (a2dp_ctrl_receive(common, &ack, 1) < 0) { + ERROR("A2DP COMMAND %s: no ACK", dump_a2dp_ctrl_event(cmd)); + return -1; + } + + INFO("A2DP COMMAND %s DONE STATUS %d", dump_a2dp_ctrl_event(cmd), ack); + + if (ack == A2DP_CTRL_ACK_INCALL_FAILURE) + return ack; + if (ack != A2DP_CTRL_ACK_SUCCESS) { + ERROR("A2DP COMMAND %s error %d", dump_a2dp_ctrl_event(cmd), ack); + return -1; + } + return 0; +} + +int check_a2dp_ready(struct a2dp_stream_common *common) +{ + INFO("state %s", dump_a2dp_hal_state(common->state)); + if (a2dp_command(common, A2DP_CTRL_CMD_CHECK_READY) < 0) + { + ERROR("check a2dp ready failed"); + return -1; + } + return 0; +} + +int a2dp_read_audio_config(struct a2dp_stream_common *common) +{ + uint32_t sample_rate; + uint8_t channel_count; + + if (a2dp_command(common, A2DP_CTRL_GET_AUDIO_CONFIG) < 0) + { + ERROR("check a2dp ready failed"); + return -1; + } + + if (a2dp_ctrl_receive(common, &sample_rate, 4) < 0) + return -1; + if (a2dp_ctrl_receive(common, &channel_count, 1) < 0) + return -1; + + common->cfg.channel_flags = (channel_count == 1 ? AUDIO_CHANNEL_IN_MONO : AUDIO_CHANNEL_IN_STEREO); + common->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT; + common->cfg.rate = sample_rate; + + INFO("got config %d %d", common->cfg.format, common->cfg.rate); + + return 0; +} + +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; + INFO("%s",__func__); + memset(p_codec_cfg,0,20); + INFO("%s",__func__); + + if (send(common->ctrl_fd, cmd, 2, MSG_NOSIGNAL) == -1) + { + ERROR("cmd failed (%s)", strerror(errno)); + skt_disconnect(common->ctrl_fd); + common->ctrl_fd = AUDIO_SKT_DISCONNECTED; + return -1; + } + + if (a2dp_ctrl_receive(common, &ack, 1) < 0) + return -1; + + if (ack != A2DP_CTRL_ACK_SUCCESS) + { + ERROR("%s: Failed to get ack",__func__); + return -1; + } + if (a2dp_ctrl_receive(common, &len, 1) < 0) + return -1; + if (a2dp_ctrl_receive(common, p_codec_cfg, len) < 0) + return -1; + + INFO("got codec config"); + p_codec_cfg = common->codec_cfg; + + for (i=0;ictrl_fd); + if (a2dp_ctrl_receive(common, mcast_status, 1) < 0) + return -1; + if (a2dp_ctrl_receive(common, num_dev, 1) < 0) + return -1; + INFO("%s: multicast status = %d, num_dev = %d",__func__,*mcast_status,*num_dev); + return 0; +} + +void a2dp_open_ctrl_path(struct a2dp_stream_common *common) +{ + int i; + + /* retry logic to catch any timing variations on control channel */ + for (i = 0; i < CTRL_CHAN_RETRY_COUNT; i++) + { + /* connect control channel if not already connected */ + if ((common->ctrl_fd = skt_connect(A2DP_CTRL_PATH, common->buffer_sz)) > 0) + { + /* success, now check if stack is ready */ + if (check_a2dp_ready(common) == 0) + break; + + ERROR("error : a2dp not ready, wait 250 ms and retry"); + usleep(250000); + skt_disconnect(common->ctrl_fd); + common->ctrl_fd = AUDIO_SKT_DISCONNECTED; + } + + /* ctrl channel not ready, wait a bit */ + usleep(250000); + } +} + +/***************************************************************************** +** +** AUDIO DATA PATH +** +*****************************************************************************/ + +void a2dp_stream_common_init(struct a2dp_stream_common *common) +{ + pthread_mutexattr_t lock_attr; + + FNLOG(); + + pthread_mutexattr_init(&lock_attr); + pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&common->lock, &lock_attr); + + common->ctrl_fd = AUDIO_SKT_DISCONNECTED; + common->audio_fd = AUDIO_SKT_DISCONNECTED; + common->state = AUDIO_A2DP_STATE_STOPPED; + + /* manages max capacity of socket pipe */ + common->buffer_sz = AUDIO_STREAM_OUTPUT_BUFFER_SZ; + bt_split_a2dp_enabled = false; +} + +int start_audio_datapath(struct a2dp_stream_common *common) +{ + INFO("state %d", common->state); + + #ifdef BT_AUDIO_SYSTRACE_LOG + char trace_buf[512]; + #endif + + INFO("state %s", dump_a2dp_hal_state(common->state)); + + int oldstate = common->state; + common->state = AUDIO_A2DP_STATE_STARTING; + + int a2dp_status = a2dp_command(common, A2DP_CTRL_CMD_START); + #ifdef BT_AUDIO_SYSTRACE_LOG + snprintf(trace_buf, 32, "start_audio_data_path:"); + if (PERF_SYSTRACE) + { + ATRACE_BEGIN(trace_buf); + } + #endif + + + #ifdef BT_AUDIO_SYSTRACE_LOG + if (PERF_SYSTRACE) + { + ATRACE_END(); + } + #endif + if (a2dp_status < 0) + { + ERROR("%s Audiopath start failed (status %d)", __func__, a2dp_status); + goto error; + } + else if (a2dp_status == A2DP_CTRL_ACK_INCALL_FAILURE) + { + ERROR("%s Audiopath start failed - in call, move to suspended", __func__); + goto error; + } + if (!bt_split_a2dp_enabled) + { + /* connect socket if not yet connected */ + if (common->audio_fd == AUDIO_SKT_DISCONNECTED) + { + common->audio_fd = skt_connect(A2DP_DATA_PATH, common->buffer_sz); + if (common->audio_fd < 0) + { + common->state = oldstate; + goto error; + } + + common->state = AUDIO_A2DP_STATE_STARTED; + } + } + else + { + common->state = AUDIO_A2DP_STATE_STARTED; + } + + return 0; +error: + common->state = oldstate; + return -1; +} + +int stop_audio_datapath(struct a2dp_stream_common *common) +{ + int oldstate = common->state; + + INFO("state %s", dump_a2dp_hal_state(common->state)); + + if (common->ctrl_fd == AUDIO_SKT_DISCONNECTED) + return -1; + + /* prevent any stray output writes from autostarting the stream + while stopping audiopath */ + common->state = AUDIO_A2DP_STATE_STOPPING; + + if (a2dp_command(common, A2DP_CTRL_CMD_STOP) < 0) + { + ERROR("audiopath stop failed"); + common->state = oldstate; + return -1; + } + + common->state = AUDIO_A2DP_STATE_STOPPED; + + if (!bt_split_a2dp_enabled) + { + /* disconnect audio path */ + skt_disconnect(common->audio_fd); + common->audio_fd = AUDIO_SKT_DISCONNECTED; + } + + return 0; +} + +int suspend_audio_datapath(struct a2dp_stream_common *common, bool standby) +{ + INFO("state %s", dump_a2dp_hal_state(common->state)); + + + if (common->state == AUDIO_A2DP_STATE_STOPPING) + return -1; + + if (a2dp_command(common, A2DP_CTRL_CMD_SUSPEND) < 0) + return -1; + + if (standby) + common->state = AUDIO_A2DP_STATE_STANDBY; + else + common->state = AUDIO_A2DP_STATE_SUSPENDED; + + if (!bt_split_a2dp_enabled) + { + /* disconnect audio path */ + skt_disconnect(common->audio_fd); + + common->audio_fd = AUDIO_SKT_DISCONNECTED; + } + + return 0; +} + +int check_a2dp_stream_started(struct a2dp_stream_common *common) +{ + if (a2dp_command(common, A2DP_CTRL_CMD_CHECK_STREAM_STARTED) < 0) + { + INFO("Btif not in stream state"); + return -1; + } + return 0; +} + +int audio_open_ctrl_path() +{ + INFO("%s",__func__); + a2dp_open_ctrl_path(&audio_stream); + if (audio_stream.ctrl_fd != AUDIO_SKT_DISCONNECTED) + { + INFO("control path opend successfull"); + return 0; + } + else + INFO("control path opend successfull"); + return -1; +} + +int audio_start_stream() +{ + int i; + INFO("%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state)); + + if (audio_stream.state == AUDIO_A2DP_STATE_SUSPENDED) + { + INFO("stream suspended"); + return -1; + } + + for (i = 0; i < STREAM_START_MAX_RETRY_COUNT; i++) + { + if (start_audio_datapath(&audio_stream) == 0) + { + INFO("a2dp stream started successfully"); + break; + } + if (audio_stream.ctrl_fd == AUDIO_SKT_DISCONNECTED) + { + INFO("control path is disconnected"); + break; + } + INFO("%s: a2dp stream not started,wait 100mse & retry"); + usleep(100000); + } + if (audio_stream.state != AUDIO_A2DP_STATE_STARTED) + { + ERROR("Failed to start a2dp stream"); + return -1; + } + return 0; +} + +int audio_stream_open() +{ + INFO("%s",__func__); + a2dp_stream_common_init(&audio_stream); + a2dp_open_ctrl_path(&audio_stream); + bt_split_a2dp_enabled = true; + if (audio_stream.ctrl_fd != AUDIO_SKT_DISCONNECTED) + { + INFO("control path open successful"); + /*Delay to ensure Headset is in proper state when START is initiated + from DUT immediately after the connection due to ongoing music playback. */ + usleep(250000); + a2dp_command(&audio_stream,A2DP_CTRL_CMD_OFFLOAD_SUPPORTED); + return 0; + } + else + INFO("control path open failed"); + + return -1; +} + +int audio_stream_close() +{ + INFO("%s",__func__); + + if (audio_stream.state == AUDIO_A2DP_STATE_STARTED || + audio_stream.state == AUDIO_A2DP_STATE_STOPPING) + { + INFO("%s: Suspending audio stream",__func__); + suspend_audio_datapath(&audio_stream,true); + } + + skt_disconnect(audio_stream.ctrl_fd); + audio_stream.ctrl_fd = AUDIO_SKT_DISCONNECTED; + return 0; +} +int audio_stop_stream() +{ + INFO("%s",__func__); + if (suspend_audio_datapath(&audio_stream, true) == 0) + { + INFO("audio start stream successful"); + return 0; + } + audio_stream.state = AUDIO_A2DP_STATE_STOPPED; + return -1; +} + +int audio_suspend_stream() +{ + INFO("%s",__func__); + if (suspend_audio_datapath(&audio_stream, false) == 0) + { + INFO("audio start stream successful"); + return 0; + } + return -1; +} + +void audio_handoff_triggered() +{ + INFO("%s state = %s",__func__,dump_a2dp_hal_state(audio_stream.state)); + if (audio_stream.state != AUDIO_A2DP_STATE_STOPPED || + audio_stream.state != AUDIO_A2DP_STATE_STOPPING) + { + audio_stream.state = AUDIO_A2DP_STATE_STOPPED; + } +} + +void clear_a2dpsuspend_flag() +{ + INFO("%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state)); + if (audio_stream.state == AUDIO_A2DP_STATE_SUSPENDED) + audio_stream.state = AUDIO_A2DP_STATE_STOPPED; +} + +void * audio_get_codec_config(uint8_t *multicast_status, uint8_t *num_dev, + audio_format_t *codec_type) +{ + char *p_common_cfg = &audio_stream.codec_cfg[0]; + int i; + + INFO("%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state)); + + a2dp_get_multicast_status(&audio_stream, multicast_status,num_dev); + + DEBUG("got multicast status = %d dev = %d",*multicast_status,*num_dev); + if (a2dp_read_codec_config(&audio_stream, 0) == 0) + { + return (a2dp_codec_parser(&audio_stream.codec_cfg[0], codec_type)); + } + return NULL; +} + +void* audio_get_next_codec_config(uint8_t idx, audio_format_t *codec_type) +{ + INFO("%s",__func__); + if (a2dp_read_codec_config(&audio_stream,idx) == 0) + { + return a2dp_codec_parser(&audio_stream.codec_cfg[0], codec_type); + } + return NULL; +} +//Entry point for dynamic lib +const bt_host_ipc_interface_t BTHOST_IPC_INTERFACE = { + sizeof(bt_host_ipc_interface_t), + a2dp_open_ctrl_path, + a2dp_stream_common_init, + start_audio_datapath, + suspend_audio_datapath, + stop_audio_datapath, + check_a2dp_stream_started, + check_a2dp_ready, + a2dp_read_audio_config, + skt_read, + skt_write, + skt_disconnect, + a2dp_command, + audio_stream_open, + audio_stream_close, + audio_start_stream, + audio_stop_stream, + audio_suspend_stream, + audio_get_codec_config, + audio_handoff_triggered, + clear_a2dpsuspend_flag, + audio_get_next_codec_config +}; diff --git a/audio_a2dp_hw/bthost_ipc.h b/audio_a2dp_hw/bthost_ipc.h new file mode 100644 index 000000000..5ee0a719b --- /dev/null +++ b/audio_a2dp_hw/bthost_ipc.h @@ -0,0 +1,155 @@ +/****************************************************************************** + * Copyright (C) 2016, The Linux Foundation. All rights reserved. + * + * Not a Contribution + *****************************************************************************/ +/***************************************************************************** + * 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. + * + ******************************************************************************/ + +/***************************************************************************** + * + * Filename: audio_a2dp_hw.h + * + * Description: + * + *****************************************************************************/ +#ifndef BT_HOST_IPC_H +#define BT_HOST_IPC_H +#include "audio_a2dp_hw.h" +#include +/***************************************************************************** +** Constants & Macros +******************************************************************************/ + +#define BT_AUDIO_HARDWARE_INTERFACE "libbthost" +#define A2DP_CTRL_PATH "/data/misc/bluedroid/.a2dp_ctrl" +#define A2DP_DATA_PATH "/data/misc/bluedroid/.a2dp_data" + +typedef enum { + A2DP_CTRL_GET_CODEC_CONFIG = 11, + A2DP_CTRL_GET_MULTICAST_STATUS, +} tA2DP_CTRL_EXT_CMD; + +/* +codec specific definitions +*/ +#define CODEC_TYPE_SBC 0x00 +#define CODEC_TYPE_AAC 0x02 +#define NON_A2DP_CODEC_TYPE 0xFF +#define CODEC_OFFSET 3 +#define VENDOR_ID_OFFSET 4 +#define CODEC_ID_OFFSET (VENDOR_ID_OFFSET + 4) + +#ifndef VENDOR_APTX +#define VENDOR_APTX 0x4F +#endif +#ifndef VENDOR_APTX_HD +#define VENDOR_APTX_HD 0xD7 +#endif +#ifndef VENDOR_APTX_LL +#define VENDOR_APTX_LL 0x0A +#endif +#ifndef APTX_CODEC_ID +#define APTX_CODEC_ID 0x01 +#endif +#ifndef APTX_HD_CODEC_ID +#define APTX_HD_CODEC_ID 0x24 +#endif + +#define A2D_SBC_FREQ_MASK 0xF0 +#define A2D_SBC_CHN_MASK 0x0F +#define A2D_SBC_BLK_MASK 0xF0 +#define A2D_SBC_SUBBAND_MASK 0x0C +#define A2D_SBC_ALLOC_MASK 0x03 +#define A2D_SBC_SAMP_FREQ_16 0x80 /* b7:16 kHz */ +#define A2D_SBC_SAMP_FREQ_32 0x40 /* b6:32 kHz */ +#define A2D_SBC_SAMP_FREQ_44 0x20 /* b5:44.1kHz */ +#define A2D_SBC_SAMP_FREQ_48 0x10 /* b4:48 kHz */ +#define A2D_SBC_CH_MD_MONO 0x08 /* b3: mono */ +#define A2D_SBC_CH_MD_DUAL 0x04 /* b2: dual */ +#define A2D_SBC_CH_MD_STEREO 0x02 /* b1: stereo */ +#define A2D_SBC_CH_MD_JOINT 0x01 /* b0: joint stereo */ +#define A2D_SBC_BLOCKS_4 0x80 /* 4 blocks */ +#define A2D_SBC_BLOCKS_8 0x40 /* 8 blocks */ +#define A2D_SBC_BLOCKS_12 0x20 /* 12blocks */ +#define A2D_SBC_BLOCKS_16 0x10 /* 16blocks */ +#define A2D_SBC_SUBBAND_4 0x08 /* b3: 4 */ +#define A2D_SBC_SUBBAND_8 0x04 /* b2: 8 */ +#define A2D_SBC_ALLOC_MD_S 0x02 /* b1: SNR */ +#define A2D_SBC_ALLOC_MD_L 0x01 /* b0: loundess */ + +/* APTX bitmask helper */ +#define A2D_APTX_SAMP_FREQ_MASK 0xF0 +#define A2D_APTX_SAMP_FREQ_48 0x10 +#define A2D_APTX_SAMP_FREQ_44 0x20 +#define A2D_APTX_CHAN_MASK 0x0F +#define A2D_APTX_CHAN_STEREO 0x02 +#define A2D_APTX_CHAN_MONO 0x01 + +typedef struct { + uint8_t codec_type; + uint8_t dev_idx; + uint16_t sampling_rate; /*44.1khz,48khz*/ + uint8_t chn; /*0(Mono),1(Dual),2(Stereo),3(JS)*/ + uint8_t blk_len; /*4,8,12,16 */ + uint8_t subband; /*4,8*/ + uint8_t alloc; /*0(Loudness),1(SNR)*/ + uint8_t min_bitpool; /* 2 */ + uint8_t max_bitpool; /*53(44.1khz),51 (48khz) */ + uint16_t mtu; + uint32_t bitrate; +}tA2DP_SBC_CODEC; + +typedef struct { + uint8_t codec_type; + uint8_t dev_idx; + uint32_t vendor_id; + uint16_t codec_id; + uint16_t sampling_rate; + uint8_t chnl; + uint8_t cp; + uint16_t mtu; + uint32_t bitrate; +}tA2DP_APTX_CODEC; + +typedef struct { + /** Set to sizeof(bt_host_ipc_interface_t) */ + size_t size; + void (*a2dp_open_ctrl_path)(struct a2dp_stream_common *common); + void (*a2dp_stream_common_init)(struct a2dp_stream_common *common); + int (*start_audio_datapath)(struct a2dp_stream_common *common); + int (*suspend_audio_datapath)(struct a2dp_stream_common *common, bool standby); + int (*stop_audio_datapath)(struct a2dp_stream_common *common); + int (*check_a2dp_stream_started)(struct a2dp_stream_common *common); + int (*check_a2dp_ready)(struct a2dp_stream_common *common); + int (*a2dp_read_audio_config)(struct a2dp_stream_common *common); + int (*skt_read)(int fd,void *buf, size_t bytes); + int (*skt_write)(int fd,const void *buf, size_t bytes); + void (*skt_disconnect)(int fd); + int (*a2dp_command)(struct a2dp_stream_common *common,char cmd); + int (*audio_stream_open)(void); + int (*audio_stream_close)(void); + int (*audio_start_stream)(void); + int (*audio_stop_stream)(void); + int (*audio_suspend_stream)(void); + void* (*audio_get_codec_config)(uint8_t *mcast, uint8_t *num_dev, audio_format_t *codec_type); + void (*audio_handoff_triggered)(void); + void (*clear_a2dpsuspend_flag)(void); + void*(*audio_get_next_codec_config)(uint8_t idx, audio_format_t *codec_type); + +} bt_host_ipc_interface_t; +#endif -- 2.11.0