blob: 6d8901d6841cdb11937f6d5cb7668c71683ff99d [file] [log] [blame]
/*
* Copyright (c) 2016-2019, 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 <inttypes.h>
#include <errno.h>
#include <log/log.h>
#include <cutils/atomic.h>
#include <hardware/audio.h>
#include "sound/compress_params.h"
#include "audio_hw.h"
#include "audio_extn.h"
#include "audio_hw_extn_api.h"
#include <pthread.h>
#ifdef DYNAMIC_LOG_ENABLED
#include <log_xml_parser.h>
#define LOG_MASK HAL_MOD_FILE_AUDIO_HW_EXTN_API
#include <log_utils.h>
#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)) {
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__);
}
}