/* * Copyright (c) 2016-2017, 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 "BT_HAL_EXTN" /*#define LOG_NDEBUG 0*/ #include #include #include #include #include #include #include #include <../../../../system/bt/audio_a2dp_hw/bthost_ipc.h> #include #ifdef DYNAMIC_LOG_ENABLED #include #define LOG_MASK HAL_MOD_FILE_BT_HAL #include #endif #define DEFAULT_BUF_SIZE 6144 #define UNUSED(x) (void)(x) #define ASSERTC(cond, msg, val) if (!(cond)) {ALOGE("### ASSERT : %s line %d %s (%d) ###", __FILE__, __LINE__, msg, val);} static void *lib_handle = NULL; bt_host_ipc_interface_t *ipc_if = NULL; struct a2dp_stream_out { struct audio_stream_out stream; struct a2dp_stream_common common; }; typedef struct bt_hal_module { struct audio_hw_device *a2dp_device; struct a2dp_stream_out *a2dp_output; struct audio_config config; audio_channel_mask_t in_channel_mask; int bit_width; struct resampler_itfe *resampler; void *data; void *reformatted_buf; } bt_hal_module_t; static int bt_create_resampler(uint32_t in_rate, struct bt_hal_module *bt) { int err = 0; ALOGV("%s", __FUNCTION__); if (bt->resampler == NULL) { err = create_resampler(in_rate, bt->config.sample_rate, popcount(bt->config.channel_mask), RESAMPLER_QUALITY_DEFAULT, NULL, &bt->resampler); if (err) { ALOGE("%s: Failed to create resampler", __FUNCTION__); free(bt->resampler); bt->resampler = NULL; return -EINVAL; } ALOGV("%s: Created resampler[%p] with in_sample_rate[%d], out_sample_rate[%d]", __FUNCTION__, bt->resampler, in_rate, bt->config.sample_rate); } return 0; } static void bt_destroy_resampler(struct bt_hal_module *bt) { ALOGV("%s", __FUNCTION__); if (bt->resampler != NULL) { release_resampler(bt->resampler); bt->resampler = NULL; } if (bt->data != NULL) { free(bt->data); bt->data = NULL; } } static uint32_t out_get_latency(struct a2dp_stream_out * out) { int latency_us; ALOGV("%s",__FUNCTION__); latency_us = ((out->common.buffer_sz * 1000 ) / audio_stream_out_frame_size(&out->stream) / out->common.cfg.rate) * 1000; return (latency_us / 1000) + 200; } static int calc_audiotime(struct a2dp_config cfg, int bytes) { int chan_count = popcount(cfg.channel_flags); int bytes_per_sample = 4; ASSERTC(cfg.format == AUDIO_FORMAT_PCM_8_24_BIT, "unsupported sample sz", cfg.format); return (int)(((int64_t)bytes * (1000000 / (chan_count * bytes_per_sample))) / cfg.rate); } static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes) { struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream; int sent; int us_delay; ALOGV("write %zu bytes (fd %d)", bytes, out->common.audio_fd); pthread_mutex_lock(&out->common.lock); if (out->common.state == AUDIO_A2DP_STATE_SUSPENDED || out->common.state == AUDIO_A2DP_STATE_STOPPING) { ALOGV("stream suspended or closing"); goto error; } /* only allow autostarting if we are in stopped or standby */ if ((out->common.state == AUDIO_A2DP_STATE_STOPPED) || (out->common.state == AUDIO_A2DP_STATE_STANDBY)) { if (ipc_if->start_audio_datapath(&out->common) < 0) { goto error; } } else if (out->common.state != AUDIO_A2DP_STATE_STARTED) { ALOGE("%s: stream not in stopped or standby", __FUNCTION__); goto error; } pthread_mutex_unlock(&out->common.lock); sent = ipc_if->skt_write(out->common.audio_fd, buffer, bytes); pthread_mutex_lock(&out->common.lock); if (sent == -1) { ipc_if->skt_disconnect(out->common.audio_fd); out->common.audio_fd = AUDIO_SKT_DISCONNECTED; if ((out->common.state != AUDIO_A2DP_STATE_SUSPENDED) && (out->common.state != AUDIO_A2DP_STATE_STOPPING)) { out->common.state = AUDIO_A2DP_STATE_STOPPED; } else { ALOGE("%s: write failed : stream suspended, avoid resetting state", __FUNCTION__); } goto error; } pthread_mutex_unlock(&out->common.lock); return bytes; error: pthread_mutex_unlock(&out->common.lock); us_delay = calc_audiotime(out->common.cfg, bytes); usleep(us_delay); return bytes; } static audio_format_t out_get_format(const struct audio_stream *stream) { struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream; ALOGV("format 0x%x", out->common.cfg.format); return out->common.cfg.format; } static uint32_t out_get_channels(const struct audio_stream *stream) { struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream; ALOGV("channels 0x%" PRIx32, out->common.cfg.channel_flags); return out->common.cfg.channel_flags; } int audio_extn_bt_hal_load(void **handle) { hw_module_t *mod; int status = 0; struct bt_hal_module *bt = NULL; ALOGV("%s", __FUNCTION__); if (handle == NULL) { status = -EINVAL; goto EXIT; } bt = malloc(sizeof(bt_hal_module_t)); if (bt == NULL){ ALOGE("%s: Memory allocation failed!", __FUNCTION__); status = -ENOMEM; goto EXIT; } *handle = bt; status = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID/*_A2DP*/, (const char*)"a2dp", (const hw_module_t**)&mod); if (status) { ALOGE("%s: Could not get a2dp hardware module", __FUNCTION__); goto EXIT; } bt->a2dp_device = calloc(1, sizeof(struct audio_hw_device)); if (!bt->a2dp_device) return -ENOMEM; bt->a2dp_device->common.module = (struct hw_module_t *) mod; if (status) { ALOGE("%s: couldn't open a2dp audio hw device", __FUNCTION__); goto EXIT; } EXIT: return status; } int audio_extn_bt_hal_unload(void *handle) { int status = 0; struct bt_hal_module *bt = NULL; ALOGV("%s", __FUNCTION__); if (handle == NULL) { status = -EINVAL; goto EXIT; } bt = (struct bt_hal_module *) handle; if (!bt->a2dp_device) { ALOGE("%s: No active A2dp output found", __FUNCTION__); goto EXIT; } if (bt->a2dp_output != NULL) { audio_extn_bt_hal_close_output_stream(bt); bt->a2dp_output = NULL; } if (bt->reformatted_buf != NULL) { free(bt->reformatted_buf); bt->reformatted_buf = NULL; } free(bt->a2dp_device); EXIT: return status; } int audio_extn_bt_hal_open_output_stream(void *handle, int in_rate, audio_channel_mask_t channel_mask, int bit_width) { int status = 0; struct bt_hal_module *bt = NULL; ALOGV("%s", __FUNCTION__); if (handle == NULL) { status = -EINVAL; goto EXIT; } bt = (struct bt_hal_module *) handle; if (bt->a2dp_device == NULL) { ALOGE("%s: Invalid device instance!", __FUNCTION__); status = -EINVAL; goto EXIT; } if (channel_mask != AUDIO_CHANNEL_OUT_STEREO && channel_mask != AUDIO_CHANNEL_OUT_MONO) { status = -EINVAL; goto EXIT; } if (bit_width != CODEC_BACKEND_DEFAULT_BIT_WIDTH) { status = -EINVAL; goto EXIT; } bt->in_channel_mask = channel_mask; bt->bit_width = bit_width; bt->a2dp_output = (struct a2dp_stream_out *)calloc(1, sizeof(struct a2dp_stream_out)); if (!bt->a2dp_output) { status = -ENOMEM; goto EXIT; } lib_handle = dlopen("libbthost_if.so", RTLD_NOW); if (!lib_handle) { ALOGV("Failed to load bthost-ipc library %s",dlerror()); status = -EINVAL; goto EXIT; } else { ipc_if = (bt_host_ipc_interface_t*) dlsym(lib_handle,"BTHOST_IPC_INTERFACE"); if (!ipc_if) { ALOGE("%s: Failed to load BT IPC library symbol", __FUNCTION__); status = -EINVAL; goto EXIT; } } bt->a2dp_output->stream.common.get_channels = out_get_channels; bt->a2dp_output->stream.common.get_format = out_get_format; bt->a2dp_output->stream.write = out_write; /* initialize a2dp specifics */ ipc_if->a2dp_stream_common_init(&bt->a2dp_output->common); bt->a2dp_output->common.cfg.channel_flags = AUDIO_STREAM_DEFAULT_CHANNEL_FLAG; bt->a2dp_output->common.cfg.format = AUDIO_FORMAT_PCM_8_24_BIT; bt->a2dp_output->common.cfg.rate = AUDIO_STREAM_DEFAULT_RATE; /* set output bt->config values */ bt->config.format = bt->a2dp_output->common.cfg.format; bt->config.sample_rate = bt->a2dp_output->common.cfg.rate; bt->config.channel_mask = bt->a2dp_output->common.cfg.channel_flags; ipc_if->a2dp_open_ctrl_path(&bt->a2dp_output->common); if (bt->a2dp_output->common.ctrl_fd == AUDIO_SKT_DISCONNECTED) { ALOGE("%s: ctrl socket failed to connect (%s)", __FUNCTION__, strerror(errno)); status = -EINVAL; goto EXIT; } if (ipc_if->a2dp_command(&bt->a2dp_output->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) { ALOGI("Streaming mode set successfully"); } /* 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); if (status) { ALOGE("%s: Failed to open output stream for a2dp: status %d", __FUNCTION__, status); goto EXIT; } else { if (in_rate != 44100) { bt_create_resampler(in_rate, bt); } if (bt->config.format != AUDIO_FORMAT_PCM_16_BIT) { bt->reformatted_buf = malloc(DEFAULT_BUF_SIZE * 4); if (bt->reformatted_buf == NULL) { ALOGE("%s: memory allocation failed!", __FUNCTION__); status = -ENOMEM; goto EXIT; } } } EXIT: if (status != 0 && bt->a2dp_output != NULL) { free(bt->a2dp_output); bt->a2dp_output = NULL; } return status; } int audio_extn_bt_hal_close_output_stream(void *handle) { int status = 0; struct bt_hal_module *bt = NULL; ALOGV("%s", __FUNCTION__); if (handle == NULL) { status = -EINVAL; goto EXIT; } bt = (struct bt_hal_module *) handle; if (bt->a2dp_device == NULL) { ALOGI("%s: No active A2dp output found", __FUNCTION__); bt->a2dp_output = NULL; goto EXIT; } if (bt->a2dp_output == NULL) { ALOGE("%s: Invalid A2DP stream instance!", __FUNCTION__); status = -EINVAL; goto EXIT; } bt_destroy_resampler(bt); pthread_mutex_lock(&bt->a2dp_output->common.lock); if ((bt->a2dp_output->common.state == AUDIO_A2DP_STATE_STARTED) || (bt->a2dp_output->common.state == AUDIO_A2DP_STATE_STOPPING)) ipc_if->stop_audio_datapath(&bt->a2dp_output->common); ipc_if->skt_disconnect(bt->a2dp_output->common.ctrl_fd); bt->a2dp_output->common.ctrl_fd = AUDIO_SKT_DISCONNECTED; if (lib_handle) dlclose(lib_handle); free(bt->a2dp_output); pthread_mutex_unlock(&bt->a2dp_output->common.lock); bt->a2dp_output = NULL; if (bt->reformatted_buf != NULL) { free(bt->reformatted_buf); bt->reformatted_buf = NULL; } EXIT: return status; } int audio_extn_bt_hal_out_write(void *handle, void *buf, int size) { int status = 0; size_t in_frames = 0; size_t out_frames = 0; struct bt_hal_module *bt = NULL; ALOGV("%s", __FUNCTION__); if (handle == NULL) { status = -EINVAL; goto EXIT; } bt = (struct bt_hal_module *) handle; in_frames = size/(popcount(bt->in_channel_mask) * (bt->bit_width / 8)); if (bt->resampler != NULL) { if (bt->data == NULL) { bt->data = malloc(size); if (bt->data == NULL){ ALOGE("%s: Memory Allocation failed!", __FUNCTION__); status = -EINVAL; goto EXIT; } } out_frames = in_frames; bt->resampler->resample_from_input(bt->resampler, (int16_t *)buf, &in_frames, (int16_t *)bt->data, &out_frames); if (out_frames > 0) { ALOGV("%s: writing %d bytes to BT device", __FUNCTION__, (int) (out_frames * popcount(bt->in_channel_mask) * (bt->bit_width / 8))); if (bt->reformatted_buf != NULL) { if (size > DEFAULT_BUF_SIZE) { bt->reformatted_buf = realloc(bt->reformatted_buf, size * 4); if (bt->reformatted_buf == NULL) { status = -ENOMEM; goto EXIT; } } memcpy_by_audio_format(bt->reformatted_buf, bt->config.format, bt->data, AUDIO_FORMAT_PCM_16_BIT, out_frames * popcount(bt->in_channel_mask)); (bt->a2dp_output)->stream.write(&bt->a2dp_output->stream, bt->reformatted_buf, out_frames * popcount(bt->in_channel_mask) * (bt->bit_width/8) * 2); } else { (bt->a2dp_output)->stream.write(&bt->a2dp_output->stream, bt->data, (out_frames * popcount(bt->in_channel_mask) * (bt->bit_width / 8))); } } } else { if (bt->reformatted_buf != NULL) { if (size > DEFAULT_BUF_SIZE) { bt->reformatted_buf = realloc(bt->reformatted_buf, size * 4); if (bt->reformatted_buf == NULL) { status = -ENOMEM; goto EXIT; } } memcpy_by_audio_format(bt->reformatted_buf, bt->config.format, buf, AUDIO_FORMAT_PCM_16_BIT, in_frames * popcount(bt->in_channel_mask)); (bt->a2dp_output)->stream.write(&bt->a2dp_output->stream, bt->reformatted_buf, size * 2); } else { (bt->a2dp_output)->stream.write(&bt->a2dp_output->stream, buf, size); } } EXIT: return status; } int audio_extn_bt_hal_get_latency(void *handle) { int status = 0; struct bt_hal_module *bt = NULL; ALOGV("%s", __FUNCTION__); if (handle == NULL) { status = -EINVAL; } bt = (struct bt_hal_module *) handle; if (bt->a2dp_output != NULL) { status = out_get_latency(bt->a2dp_output); } else { status = -EINVAL; } return status; } struct audio_stream_out *audio_extn_bt_hal_get_output_stream(void *handle) { struct bt_hal_module *bt = NULL; ALOGV("%s", __FUNCTION__); if (handle == NULL) { return NULL; } bt = (struct bt_hal_module *) handle; return (bt->a2dp_output == NULL)? NULL: &bt->a2dp_output->stream; } void *audio_extn_bt_hal_get_device(void *handle) { int status = 0; struct bt_hal_module *bt = NULL; ALOGV("%s", __FUNCTION__); if (handle == NULL) { status = -EINVAL; goto EXIT; } bt = (struct bt_hal_module *) handle; return bt->a2dp_device; EXIT: return NULL; }