/****************************************************************************** * 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" #ifdef LOG_TAG #undef LOG_TAG #endif #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 80 /* Retry for 8sec to address IOT issue*/ #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; audio_aac_encoder_config aac_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) CASE_RETURN_STR(A2DP_CTRL_GET_CONNECTION_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_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)); 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("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)); 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: ERROR("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: ERROR("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: ERROR("Unknow channel mode"); } byte = *p_cfg++; //Move to VBR byte len--; switch (byte & A2D_AAC_IE_VBR_MSK) { case A2D_AAC_IE_VBR: break; default: ERROR("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; INFO("AAC: Done copying full codec config"); return ((void *)(&aac_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) { p_cfg += 4; 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); INFO("APTx: Done copying full codec config"); 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,MAX_CODEC_CFG_SIZE); 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; } static int check_a2dp_open_ready(struct a2dp_stream_common *common) { if (a2dp_command(common, A2DP_CTRL_GET_CONNECTION_STATUS) < 0) { INFO("No active a2dp connection"); 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 opened successfull"); return 0; } else INFO("control path open failed"); 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; } /* Sanity check if the ctrl_fd is valid. If audio_stream_close is not called * from audio hal previously when BT is turned off or device is disconnecte, * and tries to start stream again. */ if (check_a2dp_open_ready(&audio_stream) < 0) { if (audio_stream.ctrl_fd != AUDIO_SKT_DISCONNECTED) { ERROR("BTIF is not ready to start stream"); return -1; } /* Try to start stream to recover from ctrl skt disconnect*/ } 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", __func__); 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 stop 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) { 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 };