/* * Copyright (c) 2016-2017, 2018, 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 "qahwi" /*#define LOG_NDEBUG 0*/ #define LOG_NDDEBUG 0 #include #include #include #include #include #include "sound/compress_params.h" #include "audio_hw.h" #include "audio_extn.h" #include "audio_hw_extn_api.h" #ifdef DYNAMIC_LOG_ENABLED #include #define LOG_MASK HAL_MOD_FILE_AUDIO_HW_EXTN_API #include #endif /* default timestamp metadata definition if not defined in kernel*/ #ifndef COMPRESSED_TIMESTAMP_FLAG #define COMPRESSED_TIMESTAMP_FLAG 0 struct snd_codec_metadata { uint64_t timestamp; }; #endif static void lock_output_stream(struct stream_out *out) { pthread_mutex_lock(&out->pre_lock); pthread_mutex_lock(&out->lock); pthread_mutex_unlock(&out->pre_lock); } /* API to send playback stream specific config parameters */ int qahwi_out_set_param_data(struct audio_stream_out *stream, audio_extn_param_id param_id, audio_extn_param_payload *payload) { int ret = 0; struct stream_out *out = (struct stream_out *)stream; /* call qaf extn set_param if needed */ if (audio_extn_is_qaf_stream(out)) { /* qaf acquires out->lock internally*/ ret = audio_extn_qaf_out_set_param_data(out, param_id, payload); if (ret) ALOGE("%s::qaf_out_set_param_data failed error %d", __func__ , ret); } else { if (out->standby && (param_id != AUDIO_EXTN_PARAM_OUT_CHANNEL_MAP)) out->stream.write(&out->stream, NULL, 0); lock_output_stream(out); ret = audio_extn_out_set_param_data(out, param_id, payload); if (ret) ALOGE("%s::audio_extn_out_set_param_data error %d", __func__, ret); pthread_mutex_unlock(&out->lock); } return ret; } /* API to get playback stream specific config parameters */ int qahwi_out_get_param_data(struct audio_stream_out *stream, audio_extn_param_id param_id, audio_extn_param_payload *payload) { int ret; struct stream_out *out = (struct stream_out *)stream; /* call qaf extn set_param if enabled */ if (audio_extn_is_qaf_stream(out)) { /* qaf acquires out->lock internally*/ ret = audio_extn_qaf_out_get_param_data(out, param_id, payload); if (ret) ALOGE("%s::qaf_out_get_param_data failed error %d", __func__, ret); } else { if (out->standby) out->stream.write(&out->stream, NULL, 0); lock_output_stream(out); ret = audio_extn_out_get_param_data(out, param_id, payload); if (ret) ALOGE("%s::audio_extn_out_get_param_data failed error %d",__func__, ret); pthread_mutex_unlock(&out->lock); } return ret; } int qahwi_get_param_data(const struct audio_hw_device *adev, audio_extn_param_id param_id, audio_extn_param_payload *payload) { int ret = 0; const struct audio_device *dev = (const struct audio_device *)adev; if (adev == NULL) { ALOGE("%s::INVALID PARAM adev\n",__func__); return -EINVAL; } if (payload == NULL) { ALOGE("%s::INVALID PAYLOAD VALUE\n",__func__); return -EINVAL; } switch (param_id) { case AUDIO_EXTN_PARAM_SOUND_FOCUS: ret = audio_extn_get_soundfocus_data(dev, (struct sound_focus_param *)payload); break; case AUDIO_EXTN_PARAM_SOURCE_TRACK: ret = audio_extn_get_sourcetrack_data(dev, (struct source_tracking_param*)payload); break; case AUDIO_EXTN_PARAM_LICENSE_PARAMS: ret = audio_extn_utils_get_license_params(dev, (struct audio_license_params *)(payload)); break; default: ALOGE("%s::INVALID PARAM ID:%d\n",__func__,param_id); ret = -EINVAL; break; } return ret; } int qahwi_set_param_data(struct audio_hw_device *adev, audio_extn_param_id param_id, audio_extn_param_payload *payload) { int ret = 0; struct audio_device *dev = (struct audio_device *)adev; if (adev == NULL) { ALOGE("%s::INVALID PARAM adev\n",__func__); return -EINVAL; } if (payload == NULL) { ALOGE("%s::INVALID PAYLOAD VALUE\n",__func__); return -EINVAL; } switch (param_id) { case AUDIO_EXTN_PARAM_SOUND_FOCUS: ret = audio_extn_set_soundfocus_data(dev, (struct sound_focus_param *)payload); break; case AUDIO_EXTN_PARAM_APTX_DEC: audio_extn_set_aptx_dec_params((struct aptx_dec_param *)payload); break; case AUDIO_EXTN_PARAM_DEVICE_CONFIG: ALOGV("%s:: Calling audio_extn_set_device_cfg_params", __func__); audio_extn_set_device_cfg_params(dev, (struct audio_device_cfg_param *)payload); break; default: ALOGE("%s::INVALID PARAM ID:%d\n",__func__,param_id); ret = -EINVAL; break; } return ret; } int qahwi_in_stop(struct audio_stream_in* stream) { struct stream_in *in = (struct stream_in *)stream; struct audio_device *adev = in->dev; ALOGD("%s processing, in %p", __func__, in); pthread_mutex_lock(&adev->lock); if (!in->standby) { if (in->pcm != NULL ) { pcm_stop(in->pcm); } else if (audio_extn_cin_attached_usecase(in->usecase)) { audio_extn_cin_stop_input_stream(in); } /* Set the atomic variable when the session is stopped */ if (android_atomic_acquire_cas(false, true, &(in->capture_stopped)) == 0) ALOGI("%s: capture_stopped bit set", __func__); } pthread_mutex_unlock(&adev->lock); return 0; } ssize_t qahwi_in_read_v2(struct audio_stream_in *stream, void* buffer, size_t bytes, uint64_t *timestamp) { struct stream_in *in = (struct stream_in *)stream; struct snd_codec_metadata *mdata = NULL; size_t mdata_size = 0, bytes_read = 0; char *buf = NULL; size_t ret = 0; if (!in->qahwi_in.is_inititalized) { ALOGE("%s: invalid state!", __func__); return -EINVAL; } if (COMPRESSED_TIMESTAMP_FLAG && ((in->flags & AUDIO_INPUT_FLAG_TIMESTAMP) || (in->flags & AUDIO_INPUT_FLAG_PASSTHROUGH))) { if (bytes != in->stream.common.get_buffer_size(&stream->common)) { ALOGE("%s: bytes requested must be fragment size in timestamp mode!", __func__); return -EINVAL; } mdata_size = sizeof(struct snd_codec_metadata); buf = (char *) in->qahwi_in.ibuf; ret = in->qahwi_in.base.read(&in->stream, (void *)buf, bytes + mdata_size); if (ret == bytes + mdata_size) { mdata = (struct snd_codec_metadata *) buf; bytes_read = mdata->length; if (bytes_read > bytes) { ALOGE("%s: bytes requested to small (given %zu, required %zu)", __func__, bytes, bytes_read); return -EINVAL; } memcpy(buffer, buf + mdata_size, bytes_read); if (timestamp) { *timestamp = mdata->timestamp; } } else { ALOGE("%s: error! read returned %zd", __func__, ret); } } else { bytes_read = in->qahwi_in.base.read(stream, buffer, bytes); if (timestamp) *timestamp = (uint64_t ) -1; } ALOGV("%s: flag 0x%x, bytes %zd, read %zd, ret %zd", __func__, in->flags, bytes, bytes_read, ret); return bytes_read; } static void qahwi_close_input_stream(struct audio_hw_device *dev, struct audio_stream_in *stream_in) { struct audio_device *adev = (struct audio_device *) dev; struct stream_in *in = (struct stream_in *)stream_in; ALOGV("%s", __func__); if (!adev->qahwi_dev.is_inititalized || !in->qahwi_in.is_inititalized) { ALOGE("%s: invalid state!", __func__); return; } if (in->qahwi_in.ibuf) free(in->qahwi_in.ibuf); adev->qahwi_dev.base.close_input_stream(dev, stream_in); } static int qahwi_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in, audio_input_flags_t flags, const char *address, audio_source_t source) { struct audio_device *adev = (struct audio_device *) dev; struct stream_in *in = NULL; size_t buf_size = 0, mdata_size = 0; int ret = 0; ALOGV("%s: dev_init %d, flags 0x%x", __func__, adev->qahwi_dev.is_inititalized, flags); if (!adev->qahwi_dev.is_inititalized) { ALOGE("%s: invalid state!", __func__); return -EINVAL; } ret = adev->qahwi_dev.base.open_input_stream(dev, handle, devices, config, stream_in, flags, address, source); if (ret) return ret; in = (struct stream_in *)*stream_in; // keep adev fptrs before overriding in->qahwi_in.base = in->stream; in->qahwi_in.is_inititalized = true; if (COMPRESSED_TIMESTAMP_FLAG && ((in->flags & AUDIO_INPUT_FLAG_TIMESTAMP) || (in->flags & AUDIO_INPUT_FLAG_PASSTHROUGH))) { // set read to NULL as this is not supported in timestamp mode in->stream.read = NULL; mdata_size = sizeof(struct snd_codec_metadata); buf_size = mdata_size + in->qahwi_in.base.common.get_buffer_size(&in->stream.common); in->qahwi_in.ibuf = malloc(buf_size); if (!in->qahwi_in.ibuf) { ALOGE("%s: allocation failed for timestamp metadata!", __func__); qahwi_close_input_stream(dev, &in->stream); *stream_in = NULL; ret = -ENOMEM; } ALOGD("%s: ibuf %p, buff_size %zd", __func__, in->qahwi_in.ibuf, buf_size); } return ret; } ssize_t qahwi_out_write_v2(struct audio_stream_out *stream, const void* buffer, size_t bytes, int64_t* timestamp) { struct stream_out *out = (struct stream_out *)stream; struct snd_codec_metadata *mdata = NULL; size_t mdata_size = 0, bytes_written = 0; char *buf = NULL; ssize_t ret = 0; if (!out->qahwi_out.is_inititalized) { ALOGE("%s: invalid state!", __func__); return -EINVAL; } if (COMPRESSED_TIMESTAMP_FLAG && (out->flags & AUDIO_OUTPUT_FLAG_TIMESTAMP)) { mdata_size = sizeof(struct snd_codec_metadata); buf = (char *) out->qahwi_out.obuf; if (timestamp) { mdata = (struct snd_codec_metadata *) buf; mdata->length = bytes; mdata->offset = mdata_size; mdata->timestamp = *timestamp; } memcpy(buf + mdata_size, buffer, bytes); ret = out->qahwi_out.base.write(&out->stream, (void *)buf, out->qahwi_out.buf_size); if (ret <= 0) { ALOGE("%s: error! write returned %zd", __func__, ret); } else { bytes_written = bytes; } ALOGV("%s: flag 0x%x, bytes %zd, read %zd, ret %zd timestamp 0x%"PRIx64"", __func__, out->flags, bytes, bytes_written, ret, timestamp == NULL ? 0 : *timestamp); } else { bytes_written = out->qahwi_out.base.write(&out->stream, buffer, bytes); ALOGV("%s: flag 0x%x, bytes %zd, read %zd, ret %zd", __func__, out->flags, bytes, bytes_written, ret); } return bytes_written; } static void qahwi_close_output_stream(struct audio_hw_device *dev, struct audio_stream_out *stream_out) { struct audio_device *adev = (struct audio_device *) dev; struct stream_out *out = (struct stream_out *)stream_out; ALOGV("%s", __func__); if (!adev->qahwi_dev.is_inititalized || !out->qahwi_out.is_inititalized) { ALOGE("%s: invalid state!", __func__); return; } if (out->qahwi_out.obuf) free(out->qahwi_out.obuf); out->qahwi_out.buf_size = 0; adev->qahwi_dev.base.close_output_stream(dev, stream_out); } static int qahwi_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out, const char *address) { struct audio_device *adev = (struct audio_device *) dev; struct stream_out *out = NULL; size_t buf_size = 0, mdata_size = 0; int ret = 0; ALOGV("%s: dev_init %d, flags 0x%x", __func__, adev->qahwi_dev.is_inititalized, flags); if (!adev->qahwi_dev.is_inititalized) { ALOGE("%s: invalid state!", __func__); return -EINVAL; } if (flags & AUDIO_OUTPUT_FLAG_DIRECT_PCM) flags = (flags & ~AUDIO_OUTPUT_FLAG_DIRECT_PCM ) | AUDIO_OUTPUT_FLAG_DIRECT; ret = adev->qahwi_dev.base.open_output_stream(dev, handle, devices, flags, config, stream_out, address); if (ret) return ret; out = (struct stream_out *)*stream_out; // keep adev fptrs before overriding out->qahwi_out.base = out->stream; out->qahwi_out.is_inititalized = true; if (COMPRESSED_TIMESTAMP_FLAG && (flags & AUDIO_OUTPUT_FLAG_TIMESTAMP)) { // set write to NULL as this is not supported in timestamp mode out->stream.write = NULL; mdata_size = sizeof(struct snd_codec_metadata); buf_size = out->qahwi_out.base.common.get_buffer_size(&out->stream.common); buf_size += mdata_size; out->qahwi_out.buf_size = buf_size; out->qahwi_out.obuf = malloc(buf_size); if (!out->qahwi_out.obuf) { ALOGE("%s: allocation failed for timestamp metadata!", __func__); qahwi_close_output_stream(dev, &out->stream); *stream_out = NULL; ret = -ENOMEM; } ALOGD("%s: obuf %p, buff_size %zd", __func__, out->qahwi_out.obuf, buf_size); } return ret; } int qahwi_loopback_set_param_data(audio_patch_handle_t handle, audio_extn_loopback_param_id param_id, audio_extn_loopback_param_payload *payload) { int ret = 0; ret = audio_extn_hw_loopback_set_param_data( handle, param_id, payload); return ret; } void qahwi_init(hw_device_t *device) { struct audio_device *adev = (struct audio_device *) device; ALOGD("%s", __func__); // keep adev fptrs before overriding, // as it might be used internally by overriding implementation adev->qahwi_dev.base = adev->device; adev->device.open_input_stream = qahwi_open_input_stream; adev->device.close_input_stream = qahwi_close_input_stream; adev->device.open_output_stream = qahwi_open_output_stream; adev->device.close_output_stream = qahwi_close_output_stream; adev->qahwi_dev.is_inititalized = true; } void qahwi_deinit(hw_device_t *device) { struct audio_device *adev = (struct audio_device *) device; ALOGV("%s: is_initialized %d", __func__, adev->qahwi_dev.is_inititalized); if (!adev->qahwi_dev.is_inititalized) { ALOGE("%s: invalid state!", __func__); } }