Merge "qahw: test: Add condition check for fwrite" into audio-userspace.lnx.2.2-dev
diff --git a/Makefile.am b/Makefile.am
index 57c0c82..299385d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,2 +1,7 @@
-SUBDIRS = hal qahw_api qahw_api/test
+SUBDIRS = hal
+
+if QAHW_SUPPORT
+SUBDIRS += qahw_api qahw_api/test
+endif
+
ACLOCAL_AMFLAGS = -I m4
diff --git a/configure.ac b/configure.ac
index c4c43c9..cbc082c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -56,6 +56,8 @@
AM_CONDITIONAL(USE_GLIB, test "x${with_glib}" = "xyes")
+AM_CONDITIONAL([QAHW_SUPPORT], [test x$BOARD_SUPPORTS_QAHW = xtrue])
+
AM_CONDITIONAL([HDMI_EDID], [test x$AUDIO_FEATURE_ENABLED_HDMI_EDID = xtrue])
AM_CONDITIONAL([FM_POWER_OPT], [test x$AUDIO_FEATURE_ENABLED_FM_POWER_OPT = xtrue])
AM_CONDITIONAL([USBAUDIO], [test x$AUDIO_FEATURE_ENABLED_USBAUDIO = xtrue])
@@ -78,6 +80,8 @@
AM_CONDITIONAL([ALAC_SUPPORT], [test x$AUDIO_FEATURE_ENABLED_EXTN_ALAC_DECODER = xtrue])
AM_CONDITIONAL([VORBIS_SUPPORT], [test x$AUDIO_FEATURE_ENABLED_EXTN_VORBIS_DECODER = xtrue])
AM_CONDITIONAL([WMA_SUPPORT], [test x$AUDIO_FEATURE_ENABLED_EXTN_WMA_DECODER = xtrue])
+AM_CONDITIONAL([COMPRESS_INPUT], [test x$AUDIO_FEATURE_ENABLED_COMPRESS_INPUT = xtrue])
+AM_CONDITIONAL([AUDIO_HW_EXTN_API], [test x$BOARD_SUPPORTS_QAHW = xtrue])
AC_CONFIG_FILES([ \
Makefile \
diff --git a/hal/Android.mk b/hal/Android.mk
index f971b85..570e556 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -254,6 +254,16 @@
LOCAL_SRC_FILES += audio_extn/qaf.c
endif
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_COMPRESS_INPUT)),true)
+ LOCAL_CFLAGS += -DCOMPRESS_INPUT_ENABLED
+ LOCAL_SRC_FILES += audio_extn/compress_in.c
+endif
+
+ifeq ($(strip $(BOARD_SUPPORTS_QAHW)),true)
+ LOCAL_CFLAGS += -DAUDIO_HW_EXTN_API_ENABLED
+ LOCAL_SRC_FILES += audio_hw_extn_api.c
+endif
+
LOCAL_SHARED_LIBRARIES := \
liblog \
libcutils \
@@ -262,7 +272,6 @@
libaudioroute \
libdl \
libaudioutils \
- libhardware \
libexpat
LOCAL_C_INCLUDES += \
diff --git a/hal/Makefile.am b/hal/Makefile.am
index c6e3072..6e1b9f4 100644
--- a/hal/Makefile.am
+++ b/hal/Makefile.am
@@ -123,6 +123,16 @@
AM_CFLAGS += -DCOMPRESS_METADATA_NEEDED
endif
+if COMPRESS_INPUT
+AM_CFLAGS += -DCOMPRESS_INPUT_ENABLED
+c_sources += audio_extn/compress_in.c
+endif
+
+if AUDIO_HW_EXTN_API
+AM_CFLAGS += -DAUDIO_HW_EXTN_API_ENABLED
+c_sources += audio_hw_extn_api.c
+endif
+
h_sources = audio_extn/audio_defs.h \
audio_extn/audio_extn.h \
audio_hw.h \
diff --git a/hal/audio_extn/audio_defs.h b/hal/audio_extn/audio_defs.h
index 1c5da54..ee4f7d4 100644
--- a/hal/audio_extn/audio_defs.h
+++ b/hal/audio_extn/audio_defs.h
@@ -103,4 +103,38 @@
#define AUDIO_PARAMETER_IS_HW_DECODER_SESSION_ALLOWED "is_hw_dec_session_allowed"
+/* Set or Query stream profile type */
+#define AUDIO_PARAMETER_STREAM_PROFILE "audio_stream_profile"
+
+/* audio inout flag for timestamp mode.
+ * check other input flags defined in audio.h for conflicts
+ */
+#define AUDIO_INPUT_FLAG_TIMESTAMP 0x80000000
+
+/* MAX SECTORS for sourcetracking feature */
+#define MAX_SECTORS 8
+
+struct qahw_source_tracking_param {
+ uint8_t vad[MAX_SECTORS];
+ uint16_t doa_speech;
+ uint16_t doa_noise[3];
+ uint8_t polar_activity[360];
+};
+
+struct qahw_sound_focus_param {
+ uint16_t start_angle[MAX_SECTORS];
+ uint8_t enable[MAX_SECTORS];
+ uint16_t gain_step;
+};
+
+typedef union {
+ struct qahw_source_tracking_param st_params;
+ struct qahw_sound_focus_param sf_params;
+} qahw_param_payload;
+
+typedef enum {
+ QAHW_PARAM_SOURCE_TRACK,
+ QAHW_PARAM_SOUND_FOCUS
+} qahw_param_id;
+
#endif /* AUDIO_DEFS_H */
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 07714f6..337acd1 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -501,14 +501,17 @@
void audio_extn_pm_unvote(void);
#endif
-void audio_extn_utils_update_streams_output_cfg_list(void *platform,
+void audio_extn_utils_update_streams_cfg_lists(void *platform,
struct mixer *mixer,
- struct listnode *streams_output_cfg_list);
-void audio_extn_utils_dump_streams_output_cfg_list(
- struct listnode *streams_output_cfg_list);
-void audio_extn_utils_release_streams_output_cfg_list(
- struct listnode *streams_output_cfg_list);
-void audio_extn_utils_update_stream_app_type_cfg(void *platform,
+ struct listnode *streams_output_cfg_list,
+ struct listnode *streams_input_cfg_list);
+void audio_extn_utils_dump_streams_cfg_lists(
+ struct listnode *streams_output_cfg_list,
+ struct listnode *streams_input_cfg_list);
+void audio_extn_utils_release_streams_cfg_lists(
+ struct listnode *streams_output_cfg_list,
+ struct listnode *streams_input_cfg_list);
+void audio_extn_utils_update_stream_output_app_type_cfg(void *platform,
struct listnode *streams_output_cfg_list,
audio_devices_t devices,
audio_output_flags_t flags,
@@ -516,11 +519,24 @@
uint32_t sample_rate,
uint32_t bit_width,
audio_channel_mask_t channel_mask,
+ char *profile,
+ struct stream_app_type_cfg *app_type_cfg);
+void audio_extn_utils_update_stream_input_app_type_cfg(void *platform,
+ struct listnode *streams_input_cfg_list,
+ audio_devices_t devices,
+ audio_input_flags_t flags,
+ audio_format_t format,
+ uint32_t sample_rate,
+ uint32_t bit_width,
+ char *profile,
struct stream_app_type_cfg *app_type_cfg);
int audio_extn_utils_send_app_type_cfg(struct audio_device *adev,
struct audio_usecase *usecase);
void audio_extn_utils_send_audio_calibration(struct audio_device *adev,
struct audio_usecase *usecase);
+void audio_extn_utils_update_stream_app_type_cfg_for_usecase(
+ struct audio_device *adev,
+ struct audio_usecase *usecase);
#ifdef DS2_DOLBY_DAP_ENABLED
#define LIB_DS2_DAP_HAL "vendor/lib/libhwdaphal.so"
#define SET_HW_INFO_FUNC "dap_hal_set_hw_info"
@@ -577,6 +593,8 @@
uint32_t hal_format_to_pcm(audio_format_t hal_format);
void audio_extn_utils_update_direct_pcm_fragment_size(struct stream_out *out);
+size_t audio_extn_utils_convert_format_24_8_to_8_24(void *buf, size_t bytes);
+int get_snd_codec_id(audio_format_t format);
#ifndef KPI_OPTIMIZE_ENABLED
#define audio_extn_perf_lock_init() (0)
@@ -683,4 +701,25 @@
int audio_extn_snd_mon_unregister_listener(void *stream);
#endif
+#ifdef COMPRESS_INPUT_ENABLED
+bool audio_extn_cin_applicable_stream(struct stream_in *in);
+bool audio_extn_cin_attached_usecase(audio_usecase_t uc_id);
+size_t audio_extn_cin_get_buffer_size(struct stream_in *in);
+int audio_extn_cin_start_input_stream(struct stream_in *in);
+void audio_extn_cin_stop_input_stream(struct stream_in *in);
+void audio_extn_cin_close_input_stream(struct stream_in *in);
+int audio_extn_cin_read(struct stream_in *in, void *buffer,
+ size_t bytes, size_t *bytes_read);
+int audio_extn_cin_configure_input_stream(struct stream_in *in);
+#else
+#define audio_extn_cin_applicable_stream(in) (false)
+#define audio_extn_cin_attached_usecase(uc_id) (false)
+#define audio_extn_cin_get_buffer_size(in) (0)
+#define audio_extn_cin_start_input_stream(in) (0)
+#define audio_extn_cin_stop_input_stream(in) (0)
+#define audio_extn_cin_close_input_stream(in) (0)
+#define audio_extn_cin_read(in, buffer, bytes, bytes_read) (0)
+#define audio_extn_cin_configure_input_stream(in) (0)
+#endif
+
#endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/compress_in.c b/hal/audio_extn/compress_in.c
new file mode 100644
index 0000000..403ee8b
--- /dev/null
+++ b/hal/audio_extn/compress_in.c
@@ -0,0 +1,314 @@
+/*
+* Copyright (c) 2016, 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 "audio_hw_cin"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#ifdef COMPRESS_INPUT_ENABLED
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <cutils/str_parms.h>
+#include <cutils/log.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+
+#include <hardware/audio.h>
+#include <errno.h>
+#include <time.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include "audio_extn.h"
+#include "audio_defs.h"
+#include "sound/compress_params.h"
+
+/* 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;
+};
+#define compress_config_set_timstamp_flag(config) (-ENOSYS)
+#else
+#define compress_config_set_timstamp_flag(config) \
+ (config)->codec->flags |= COMPRESSED_TIMESTAMP_FLAG
+#endif
+
+#define COMPRESS_RECORD_NUM_FRAGMENTS 8
+
+struct cin_private_data {
+ struct compr_config compr_config;
+ struct compress *compr;
+};
+
+typedef struct cin_private_data cin_private_data_t;
+
+static unsigned int cin_usecases_state;
+
+static const audio_usecase_t cin_usecases[] = {
+ USECASE_AUDIO_RECORD_COMPRESS2,
+ USECASE_AUDIO_RECORD_COMPRESS3,
+ USECASE_AUDIO_RECORD_COMPRESS4
+};
+
+static pthread_mutex_t cin_lock = PTHREAD_MUTEX_INITIALIZER;
+
+bool audio_extn_cin_applicable_stream(struct stream_in *in)
+{
+ if ((in->flags & AUDIO_INPUT_FLAG_TIMESTAMP) ||
+ in->usecase == USECASE_AUDIO_RECORD_COMPRESS2)
+ return true;
+
+ return false;
+}
+
+/* all audio_extn_cin_xxx calls must be made on an input
+ * only after validating that input against audio_extn_cin_attached_usecase
+ * except below calls
+ * 1. audio_extn_cin_applicable_stream(in)
+ * 2. audio_extn_cin_configure_input_stream(in)
+ */
+
+bool audio_extn_cin_attached_usecase(audio_usecase_t uc_id)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(cin_usecases)/
+ sizeof(cin_usecases[0]); i++) {
+ if (uc_id == cin_usecases[i] &&
+ (cin_usecases_state & (0x1 << i)))
+ return true;
+ }
+ return false;
+}
+
+static audio_usecase_t get_cin_usecase(void)
+{
+ audio_usecase_t ret_uc = USECASE_INVALID;
+ unsigned int i, num_usecase = sizeof(cin_usecases) / sizeof(cin_usecases[0]);
+ char value[PROPERTY_VALUE_MAX] = {0};
+
+ property_get("audio.record.multiple.enabled", value, NULL);
+ if (!(atoi(value) || !strncmp("true", value, 4)))
+ num_usecase = 1; /* If prop is not set, limit the num of record usecases to 1 */
+
+ ALOGV("%s: num_usecase: %d", __func__, num_usecase);
+ pthread_mutex_lock(&cin_lock);
+ for (i = 0; i < num_usecase; i++) {
+ if (!(cin_usecases_state & (0x1 << i))) {
+ cin_usecases_state |= 0x1 << i;
+ ret_uc = cin_usecases[i];
+ break;
+ }
+ }
+ pthread_mutex_unlock(&cin_lock);
+ ALOGV("%s: picked usecase: %d", __func__, ret_uc);
+ return ret_uc;
+}
+
+static void free_cin_usecase(audio_usecase_t uc_id)
+{
+ unsigned int i;
+
+ ALOGV("%s: free usecase %d", __func__, uc_id);
+ pthread_mutex_lock(&cin_lock);
+ for (i = 0; i < sizeof(cin_usecases) /
+ sizeof(cin_usecases[0]); i++) {
+ if (uc_id == cin_usecases[i])
+ cin_usecases_state &= ~(0x1 << i);
+ break;
+ }
+ pthread_mutex_unlock(&cin_lock);
+}
+
+size_t audio_extn_cin_get_buffer_size(struct stream_in *in)
+{
+ size_t sz = 0;
+ cin_private_data_t *cin_data = (cin_private_data_t *) in->cin_extn;
+
+ sz = cin_data->compr_config.fragment_size;
+ if (in->flags & AUDIO_INPUT_FLAG_TIMESTAMP)
+ sz -= sizeof(struct snd_codec_metadata);
+
+ ALOGV("%s: in %p, flags 0x%x, cin_data %p, size %zd",
+ __func__, in, in->flags, cin_data, sz);
+ return sz;
+}
+
+int audio_extn_cin_start_input_stream(struct stream_in *in)
+{
+ int ret = -EINVAL;
+ struct audio_device *adev = in->dev;
+ cin_private_data_t *cin_data = (cin_private_data_t *) in->cin_extn;
+
+ ALOGV("%s: in %p, cin_data %p", __func__, in, cin_data);
+ cin_data->compr = compress_open(adev->snd_card, in->pcm_device_id,
+ COMPRESS_OUT, &cin_data->compr_config);
+ if (cin_data->compr == NULL || !is_compress_ready(cin_data->compr)) {
+ ALOGE("%s: %s", __func__,
+ cin_data->compr ? compress_get_error(cin_data->compr) : "null");
+ if (cin_data->compr) {
+ compress_close(cin_data->compr);
+ cin_data->compr = NULL;
+ }
+ ret = -EIO;
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+
+void audio_extn_cin_stop_input_stream(struct stream_in *in)
+{
+ cin_private_data_t *cin_data = (cin_private_data_t *) in->cin_extn;
+
+ ALOGV("%s: in %p, cin_data %p", __func__, in, cin_data);
+ if (cin_data->compr) {
+ compress_close(cin_data->compr);
+ cin_data->compr = NULL;
+ }
+}
+
+void audio_extn_cin_close_input_stream(struct stream_in *in)
+{
+ cin_private_data_t *cin_data = (cin_private_data_t *) in->cin_extn;
+
+ ALOGV("%s: in %p, cin_data %p", __func__, in, cin_data);
+ if (cin_data) {
+ free(cin_data->compr_config.codec);
+ free(cin_data);
+ }
+ free_cin_usecase(in->usecase);
+}
+
+int audio_extn_cin_read(struct stream_in *in, void *buffer,
+ size_t bytes, size_t *bytes_read)
+{
+ int ret = -EINVAL;
+ size_t read_size = bytes;
+ size_t mdata_size = (sizeof(struct snd_codec_metadata));
+ cin_private_data_t *cin_data = (cin_private_data_t *) in->cin_extn;
+
+ if (cin_data->compr) {
+ /* start stream if not already done */
+ if (!is_compress_running(cin_data->compr))
+ compress_start(cin_data->compr);
+
+ if (!(in->flags & AUDIO_INPUT_FLAG_TIMESTAMP))
+ mdata_size = 0;
+
+ if (buffer && read_size) {
+ read_size = compress_read(cin_data->compr, buffer, read_size);
+ if (read_size == bytes) {
+ /* set ret to 0 if compress_read succeeded*/
+ ret = 0;
+ *bytes_read = bytes;
+ /* data from DSP comes in 24_8 format, convert it to 8_24 */
+ if (in->format == AUDIO_FORMAT_PCM_8_24_BIT) {
+ if (audio_extn_utils_convert_format_24_8_to_8_24(
+ (char *)buffer + mdata_size, bytes) != bytes)
+ ret = -EIO;
+ }
+ } else {
+ ret = errno;
+ ALOGE("%s: failed error = %d, read = %zd, err_str %s", __func__,
+ ret, read_size, compress_get_error(cin_data->compr));
+ }
+ }
+ }
+ ALOGV("%s: in %p, flags 0x%x, buf %p, bytes %zd, read_size %zd, ret %d",
+ __func__, in, in->flags, buffer, bytes, read_size, ret);
+ return ret;
+}
+
+int audio_extn_cin_configure_input_stream(struct stream_in *in)
+{
+ struct audio_device *adev = in->dev;
+ struct audio_config config = {.format = 0};
+ int ret = 0, buffer_size = 0, meta_size = sizeof(struct snd_codec_metadata);
+ cin_private_data_t *cin_data = NULL;
+
+ if (!COMPRESSED_TIMESTAMP_FLAG &&
+ (in->flags & AUDIO_INPUT_FLAG_TIMESTAMP)) {
+ ALOGE("%s: timestamp mode not supported!", __func__);
+ return -EINVAL;
+ }
+
+ cin_data = (cin_private_data_t *) calloc(1, sizeof(cin_private_data_t));
+ in->cin_extn = (void *)cin_data;
+ if (!cin_data) {
+ ALOGE("%s, allocation for private data failed!", __func__);
+ return -ENOMEM;
+ }
+
+ cin_data->compr_config.codec = (struct snd_codec *)
+ calloc(1, sizeof(struct snd_codec));
+ if (!cin_data->compr_config.codec) {
+ ALOGE("%s, allocation for codec data failed!", __func__);
+ ret = -ENOMEM;
+ goto err_config;
+ }
+
+ in->usecase = get_cin_usecase();
+ if (in->usecase == USECASE_INVALID) {
+ ALOGE("%s, Max allowed compress record usecase reached!", __func__);
+ ret = -EEXIST;
+ goto err_config;
+ }
+
+ config.sample_rate = in->sample_rate;
+ config.channel_mask = in->channel_mask;
+ config.format = in->format;
+ in->config.channels = audio_channel_count_from_in_mask(in->channel_mask);
+ buffer_size = adev->device.get_input_buffer_size(&adev->device, &config);
+
+ cin_data->compr_config.fragment_size = buffer_size;
+ cin_data->compr_config.codec->id = get_snd_codec_id(in->format);
+ cin_data->compr_config.fragments = COMPRESS_RECORD_NUM_FRAGMENTS;
+ cin_data->compr_config.codec->sample_rate = in->sample_rate;
+ cin_data->compr_config.codec->ch_in = in->config.channels;
+ cin_data->compr_config.codec->ch_out = in->config.channels;
+ cin_data->compr_config.codec->format = hal_format_to_alsa(in->format);
+ if (in->flags & AUDIO_INPUT_FLAG_TIMESTAMP) {
+ compress_config_set_timstamp_flag(&cin_data->compr_config);
+ cin_data->compr_config.fragment_size += meta_size;
+ }
+ ALOGD("%s: format %d flags 0x%x SR %d CM 0x%x buf_size %d in %p",
+ __func__, in->format, in->flags, in->sample_rate, in->channel_mask,
+ cin_data->compr_config.fragment_size, in);
+ return ret;
+
+err_config:
+ audio_extn_cin_close_input_stream(in);
+ return ret;
+}
+#endif /* COMPRESS_INPUT_ENABLED end */
diff --git a/hal/audio_extn/source_track.c b/hal/audio_extn/source_track.c
index 0a0b970..d34ed6b 100644
--- a/hal/audio_extn/source_track.c
+++ b/hal/audio_extn/source_track.c
@@ -502,6 +502,36 @@
}
}
+int audio_extn_get_soundfocus_data(const struct audio_device *adev,
+ struct sound_focus_param *payload)
+{
+ int ret = 0;
+ int bitmask =0;
+ struct sound_focus_param *sound_focus_data = payload;
+
+ bitmask = BITMASK_AUDIO_PARAMETER_KEYS_SOUND_FOCUS;
+
+ ret = get_soundfocus_sourcetracking_data(adev, bitmask,
+ sound_focus_data, NULL);
+
+ return ret ;
+}
+
+int audio_extn_get_sourcetrack_data(const struct audio_device *adev,
+ struct source_tracking_param *payload)
+{
+ int ret = 0;
+ int bitmask = 0;
+ struct source_tracking_param *source_tracking_data = payload;
+
+ bitmask = BITMASK_AUDIO_PARAMETER_KEYS_SOURCE_TRACKING;
+
+ ret = get_soundfocus_sourcetracking_data(adev, bitmask,
+ NULL, source_tracking_data);
+
+ return ret ;
+}
+
void audio_extn_source_track_get_parameters(const struct audio_device *adev,
struct str_parms *query,
struct str_parms *reply)
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index 90b39b5..b14b1e7 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -34,6 +34,7 @@
#include "platform_api.h"
#include "audio_extn.h"
#include "voice.h"
+#include "sound/compress_params.h"
#ifdef AUDIO_EXTERNAL_HDMI_ENABLED
#ifdef HDMI_PASSTHROUGH_ENABLED
@@ -43,14 +44,18 @@
#ifdef LINUX_ENABLED
#define AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE "/etc/audio_output_policy.conf"
+#define AUDIO_IO_POLICY_VENDOR_CONFIG_FILE "/etc/audio_io_policy.conf"
#else
#define AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_output_policy.conf"
+#define AUDIO_IO_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_io_policy.conf"
#endif
#define OUTPUTS_TAG "outputs"
+#define INPUTS_TAG "inputs"
#define DYNAMIC_VALUE_TAG "dynamic"
#define FLAGS_TAG "flags"
+#define PROFILES_TAG "profile"
#define FORMATS_TAG "formats"
#define SAMPLING_RATES_TAG "sampling_rates"
#define BIT_WIDTH_TAG "bit_width"
@@ -95,6 +100,12 @@
STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_INCALL_MUSIC),
#endif
STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH),
+ STRING_TO_ENUM(AUDIO_INPUT_FLAG_NONE),
+ STRING_TO_ENUM(AUDIO_INPUT_FLAG_FAST),
+ STRING_TO_ENUM(AUDIO_INPUT_FLAG_HW_HOTWORD),
+ STRING_TO_ENUM(AUDIO_INPUT_FLAG_RAW),
+ STRING_TO_ENUM(AUDIO_INPUT_FLAG_SYNC),
+ STRING_TO_ENUM(AUDIO_INPUT_FLAG_TIMESTAMP),
};
const struct string_to_enum s_format_name_to_enum_table[] = {
@@ -161,9 +172,10 @@
return 0;
}
-static audio_output_flags_t parse_flag_names(char *name)
+static audio_io_flags_t parse_flag_names(char *name)
{
uint32_t flag = 0;
+ audio_io_flags_t io_flags;
char *last_r;
char *flag_name = strtok_r(name, "|", &last_r);
while (flag_name != NULL) {
@@ -176,10 +188,12 @@
}
ALOGV("parse_flag_names: flag - %d", flag);
- return (audio_output_flags_t)flag;
+ io_flags.in_flags = (audio_input_flags_t)flag;
+ io_flags.out_flags = (audio_output_flags_t)flag;
+ return io_flags;
}
-static void parse_format_names(char *name, struct streams_output_cfg *so_info)
+static void parse_format_names(char *name, struct streams_io_cfg *s_info)
{
struct stream_format *sf_info = NULL;
char *last_r;
@@ -188,7 +202,7 @@
if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0)
return;
- list_init(&so_info->format_list);
+ list_init(&s_info->format_list);
while (str != NULL) {
audio_format_t format = (audio_format_t)string_to_enum(s_format_name_to_enum_table,
ARRAY_SIZE(s_format_name_to_enum_table), str);
@@ -199,13 +213,13 @@
break; /* return whatever was parsed */
sf_info->format = format;
- list_add_tail(&so_info->format_list, &sf_info->list);
+ list_add_tail(&s_info->format_list, &sf_info->list);
}
str = strtok_r(NULL, "|", &last_r);
}
}
-static void parse_sample_rate_names(char *name, struct streams_output_cfg *so_info)
+static void parse_sample_rate_names(char *name, struct streams_io_cfg *s_info)
{
struct stream_sample_rate *ss_info = NULL;
uint32_t sample_rate = 48000;
@@ -215,7 +229,7 @@
if (str != NULL && 0 == strcmp(str, DYNAMIC_VALUE_TAG))
return;
- list_init(&so_info->sample_rate_list);
+ list_init(&s_info->sample_rate_list);
while (str != NULL) {
sample_rate = (uint32_t)strtol(str, (char **)NULL, 10);
ALOGV("%s: sample_rate - %d", __func__, sample_rate);
@@ -226,7 +240,7 @@
return;
}
ss_info->sample_rate = sample_rate;
- list_add_tail(&so_info->sample_rate_list, &ss_info->list);
+ list_add_tail(&s_info->sample_rate_list, &ss_info->list);
}
str = strtok_r(NULL, "|", &last_r);
}
@@ -258,65 +272,82 @@
return app_type;
}
-static void update_streams_output_cfg_list(cnode *root, void *platform,
- struct listnode *streams_output_cfg_list)
+static void update_streams_cfg_list(cnode *root, void *platform,
+ struct listnode *streams_cfg_list)
{
cnode *node = root->first_child;
- struct streams_output_cfg *so_info;
+ struct streams_io_cfg *s_info;
ALOGV("%s", __func__);
- so_info = (struct streams_output_cfg *)calloc(1, sizeof(struct streams_output_cfg));
+ s_info = (struct streams_io_cfg *)calloc(1, sizeof(struct streams_io_cfg));
- if (!so_info) {
- ALOGE("failed to allocate mem for so_info list element");
+ if (!s_info) {
+ ALOGE("failed to allocate mem for s_info list element");
return;
}
while (node) {
if (strcmp(node->name, FLAGS_TAG) == 0) {
- so_info->flags = parse_flag_names((char *)node->value);
+ s_info->flags = parse_flag_names((char *)node->value);
+ } else if (strcmp(node->name, PROFILES_TAG) == 0) {
+ strlcpy(s_info->profile, (char *)node->value, sizeof(s_info->profile));
} else if (strcmp(node->name, FORMATS_TAG) == 0) {
- parse_format_names((char *)node->value, so_info);
+ parse_format_names((char *)node->value, s_info);
} else if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
- so_info->app_type_cfg.sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
- parse_sample_rate_names((char *)node->value, so_info);
+ s_info->app_type_cfg.sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ parse_sample_rate_names((char *)node->value, s_info);
} else if (strcmp(node->name, BIT_WIDTH_TAG) == 0) {
- so_info->app_type_cfg.bit_width = parse_bit_width_names((char *)node->value);
+ s_info->app_type_cfg.bit_width = parse_bit_width_names((char *)node->value);
} else if (strcmp(node->name, APP_TYPE_TAG) == 0) {
- so_info->app_type_cfg.app_type = parse_app_type_names(platform, (char *)node->value);
+ s_info->app_type_cfg.app_type = parse_app_type_names(platform, (char *)node->value);
}
node = node->next;
}
- list_add_tail(streams_output_cfg_list, &so_info->list);
+ list_add_tail(streams_cfg_list, &s_info->list);
}
-static void load_output(cnode *root, void *platform,
- struct listnode *streams_output_cfg_list)
+static void load_cfg_list(cnode *root, void *platform,
+ struct listnode *streams_output_cfg_list,
+ struct listnode *streams_input_cfg_list)
{
- cnode *node = config_find(root, OUTPUTS_TAG);
- if (node == NULL) {
- ALOGE("%s: could not load output, node is NULL", __func__);
- return;
+ cnode *node = NULL;
+
+ node = config_find(root, OUTPUTS_TAG);
+ if (node != NULL) {
+ node = node->first_child;
+ while (node) {
+ ALOGV("%s: loading output %s", __func__, node->name);
+ update_streams_cfg_list(node, platform, streams_output_cfg_list);
+ node = node->next;
+ }
+ } else {
+ ALOGI("%s: could not load output, node is NULL", __func__);
}
- node = node->first_child;
- while (node) {
- ALOGV("%s: loading output %s", __func__, node->name);
- update_streams_output_cfg_list(node, platform, streams_output_cfg_list);
- node = node->next;
+ node = config_find(root, INPUTS_TAG);
+ if (node != NULL) {
+ node = node->first_child;
+ while (node) {
+ ALOGV("%s: loading input %s", __func__, node->name);
+ update_streams_cfg_list(node, platform, streams_input_cfg_list);
+ node = node->next;
+ }
+ } else {
+ ALOGI("%s: could not load input, node is NULL", __func__);
}
}
static void send_app_type_cfg(void *platform, struct mixer *mixer,
- struct listnode *streams_output_cfg_list)
+ struct listnode *streams_output_cfg_list,
+ struct listnode *streams_input_cfg_list)
{
- int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {-1};
+ size_t app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
int length = 0, i, num_app_types = 0;
struct listnode *node;
bool update;
struct mixer_ctl *ctl = NULL;
const char *mixer_ctl_name = "App Type Config";
- struct streams_output_cfg *so_info;
+ struct streams_io_cfg *s_info = NULL;
if (!mixer) {
ALOGE("%s: mixer is null",__func__);
@@ -327,32 +358,63 @@
ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name);
return;
}
- if (streams_output_cfg_list == NULL) {
- app_type_cfg[length++] = 1;
- app_type_cfg[length++] = platform_get_default_app_type(platform);
+ app_type_cfg[length++] = num_app_types;
+
+ if (list_empty(streams_output_cfg_list)) {
+ app_type_cfg[length++] = platform_get_default_app_type_v2(platform, PCM_PLAYBACK);
app_type_cfg[length++] = 48000;
app_type_cfg[length++] = 16;
- mixer_ctl_set_array(ctl, app_type_cfg, length);
- return;
+ num_app_types += 1;
+ }
+ if (list_empty(streams_input_cfg_list)) {
+ app_type_cfg[length++] = platform_get_default_app_type_v2(platform, PCM_CAPTURE);
+ app_type_cfg[length++] = 48000;
+ app_type_cfg[length++] = 16;
+ num_app_types += 1;
}
- app_type_cfg[length++] = num_app_types;
list_for_each(node, streams_output_cfg_list) {
- so_info = node_to_item(node, struct streams_output_cfg, list);
+ s_info = node_to_item(node, struct streams_io_cfg, list);
update = true;
for (i=0; i<length; i=i+3) {
- if (app_type_cfg[i+1] == -1)
+ if (app_type_cfg[i+1] == 0)
break;
- else if (app_type_cfg[i+1] == so_info->app_type_cfg.app_type) {
+ else if (app_type_cfg[i+1] == (size_t)s_info->app_type_cfg.app_type) {
+ if (app_type_cfg[i+2] < s_info->app_type_cfg.sample_rate)
+ app_type_cfg[i+2] = s_info->app_type_cfg.sample_rate;
+ if (app_type_cfg[i+3] < s_info->app_type_cfg.bit_width)
+ app_type_cfg[i+3] = s_info->app_type_cfg.bit_width;
update = false;
break;
}
}
if (update && ((length + 3) <= MAX_LENGTH_MIXER_CONTROL_IN_INT)) {
- num_app_types += 1 ;
- app_type_cfg[length++] = so_info->app_type_cfg.app_type;
- app_type_cfg[length++] = so_info->app_type_cfg.sample_rate;
- app_type_cfg[length++] = so_info->app_type_cfg.bit_width;
+ num_app_types += 1;
+ app_type_cfg[length++] = s_info->app_type_cfg.app_type;
+ app_type_cfg[length++] = s_info->app_type_cfg.sample_rate;
+ app_type_cfg[length++] = s_info->app_type_cfg.bit_width;
+ }
+ }
+ list_for_each(node, streams_input_cfg_list) {
+ s_info = node_to_item(node, struct streams_io_cfg, list);
+ update = true;
+ for (i=0; i<length; i=i+3) {
+ if (app_type_cfg[i+1] == 0)
+ break;
+ else if (app_type_cfg[i+1] == (size_t)s_info->app_type_cfg.app_type) {
+ if (app_type_cfg[i+2] < s_info->app_type_cfg.sample_rate)
+ app_type_cfg[i+2] = s_info->app_type_cfg.sample_rate;
+ if (app_type_cfg[i+3] < s_info->app_type_cfg.bit_width)
+ app_type_cfg[i+3] = s_info->app_type_cfg.bit_width;
+ update = false;
+ break;
+ }
+ }
+ if (update && ((length + 3) <= MAX_LENGTH_MIXER_CONTROL_IN_INT)) {
+ num_app_types += 1;
+ app_type_cfg[length++] = s_info->app_type_cfg.app_type;
+ app_type_cfg[length++] = s_info->app_type_cfg.sample_rate;
+ app_type_cfg[length++] = s_info->app_type_cfg.bit_width;
}
}
ALOGV("%s: num_app_types: %d", __func__, num_app_types);
@@ -362,101 +424,131 @@
}
}
-void audio_extn_utils_update_streams_output_cfg_list(void *platform,
- struct mixer *mixer,
- struct listnode *streams_output_cfg_list)
+void audio_extn_utils_update_streams_cfg_lists(void *platform,
+ struct mixer *mixer,
+ struct listnode *streams_output_cfg_list,
+ struct listnode *streams_input_cfg_list)
{
cnode *root;
- char *data;
+ char *data = NULL;
ALOGV("%s", __func__);
list_init(streams_output_cfg_list);
- data = (char *)load_file(AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE, NULL);
- if (data == NULL) {
- send_app_type_cfg(platform, mixer, NULL);
- ALOGE("%s: could not load output policy config file", __func__);
- return;
- }
+ list_init(streams_input_cfg_list);
root = config_node("", "");
if (root == NULL) {
ALOGE("cfg_list, NULL config root");
- free(data);
return;
}
- config_load(root, data);
- load_output(root, platform, streams_output_cfg_list);
+ data = (char *)load_file(AUDIO_IO_POLICY_VENDOR_CONFIG_FILE, NULL);
+ if (data == NULL) {
+ ALOGD("%s: failed to open io config file(%s), trying older config file",
+ __func__, AUDIO_IO_POLICY_VENDOR_CONFIG_FILE);
+ data = (char *)load_file(AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE, NULL);
+ if (data == NULL) {
+ send_app_type_cfg(platform, mixer,
+ streams_output_cfg_list,
+ streams_input_cfg_list);
+ ALOGE("%s: could not load io policy config!", __func__);
+ return;
+ }
+ }
- send_app_type_cfg(platform, mixer, streams_output_cfg_list);
+ config_load(root, data);
+ load_cfg_list(root, platform, streams_output_cfg_list,
+ streams_input_cfg_list);
+
+ send_app_type_cfg(platform, mixer, streams_output_cfg_list,
+ streams_input_cfg_list);
config_free(root);
free(data);
}
-void audio_extn_utils_dump_streams_output_cfg_list(
- struct listnode *streams_output_cfg_list)
+static void audio_extn_utils_dump_streams_cfg_list(
+ struct listnode *streams_cfg_list)
{
struct listnode *node_i, *node_j;
- struct streams_output_cfg *so_info;
+ struct streams_io_cfg *s_info;
struct stream_format *sf_info;
struct stream_sample_rate *ss_info;
- ALOGV("%s", __func__);
- list_for_each(node_i, streams_output_cfg_list) {
- so_info = node_to_item(node_i, struct streams_output_cfg, list);
- ALOGV("%s: flags-%d, output_sample_rate-%d, output_bit_width-%d, app_type-%d",
- __func__, so_info->flags, so_info->app_type_cfg.sample_rate,
- so_info->app_type_cfg.bit_width, so_info->app_type_cfg.app_type);
- list_for_each(node_j, &so_info->format_list) {
+
+ list_for_each(node_i, streams_cfg_list) {
+ s_info = node_to_item(node_i, struct streams_io_cfg, list);
+ ALOGV("%s: flags-%d, sample_rate-%d, bit_width-%d, app_type-%d",
+ __func__, s_info->flags.out_flags, s_info->app_type_cfg.sample_rate,
+ s_info->app_type_cfg.bit_width, s_info->app_type_cfg.app_type);
+ list_for_each(node_j, &s_info->format_list) {
sf_info = node_to_item(node_j, struct stream_format, list);
ALOGV("format-%x", sf_info->format);
}
- list_for_each(node_j, &so_info->sample_rate_list) {
+ list_for_each(node_j, &s_info->sample_rate_list) {
ss_info = node_to_item(node_j, struct stream_sample_rate, list);
ALOGV("sample rate-%d", ss_info->sample_rate);
}
}
}
-void audio_extn_utils_release_streams_output_cfg_list(
- struct listnode *streams_output_cfg_list)
+void audio_extn_utils_dump_streams_cfg_lists(
+ struct listnode *streams_output_cfg_list,
+ struct listnode *streams_input_cfg_list)
+{
+ ALOGV("%s", __func__);
+ audio_extn_utils_dump_streams_cfg_list(streams_output_cfg_list);
+ audio_extn_utils_dump_streams_cfg_list(streams_input_cfg_list);
+}
+
+static void audio_extn_utils_release_streams_cfg_list(
+ struct listnode *streams_cfg_list)
{
struct listnode *node_i, *node_j;
- struct streams_output_cfg *so_info;
+ struct streams_io_cfg *s_info;
ALOGV("%s", __func__);
- while (!list_empty(streams_output_cfg_list)) {
- node_i = list_head(streams_output_cfg_list);
- so_info = node_to_item(node_i, struct streams_output_cfg, list);
- while (!list_empty(&so_info->format_list)) {
- node_j = list_head(&so_info->format_list);
+
+ while (!list_empty(streams_cfg_list)) {
+ node_i = list_head(streams_cfg_list);
+ s_info = node_to_item(node_i, struct streams_io_cfg, list);
+ while (!list_empty(&s_info->format_list)) {
+ node_j = list_head(&s_info->format_list);
list_remove(node_j);
free(node_to_item(node_j, struct stream_format, list));
}
- while (!list_empty(&so_info->sample_rate_list)) {
- node_j = list_head(&so_info->sample_rate_list);
+ while (!list_empty(&s_info->sample_rate_list)) {
+ node_j = list_head(&s_info->sample_rate_list);
list_remove(node_j);
free(node_to_item(node_j, struct stream_sample_rate, list));
}
list_remove(node_i);
- free(node_to_item(node_i, struct streams_output_cfg, list));
+ free(node_to_item(node_i, struct streams_io_cfg, list));
}
}
-static bool set_output_cfg(struct streams_output_cfg *so_info,
- struct stream_app_type_cfg *app_type_cfg,
- uint32_t sample_rate, uint32_t bit_width)
+void audio_extn_utils_release_streams_cfg_lists(
+ struct listnode *streams_output_cfg_list,
+ struct listnode *streams_input_cfg_list)
+{
+ ALOGV("%s", __func__);
+ audio_extn_utils_release_streams_cfg_list(streams_output_cfg_list);
+ audio_extn_utils_release_streams_cfg_list(streams_input_cfg_list);
+}
+
+static bool set_app_type_cfg(struct streams_io_cfg *s_info,
+ struct stream_app_type_cfg *app_type_cfg,
+ uint32_t sample_rate, uint32_t bit_width)
{
struct listnode *node_i;
struct stream_sample_rate *ss_info;
- list_for_each(node_i, &so_info->sample_rate_list) {
+ list_for_each(node_i, &s_info->sample_rate_list) {
ss_info = node_to_item(node_i, struct stream_sample_rate, list);
if ((sample_rate <= ss_info->sample_rate) &&
- (bit_width == so_info->app_type_cfg.bit_width)) {
+ (bit_width == s_info->app_type_cfg.bit_width)) {
- app_type_cfg->app_type = so_info->app_type_cfg.app_type;
+ app_type_cfg->app_type = s_info->app_type_cfg.app_type;
app_type_cfg->sample_rate = ss_info->sample_rate;
- app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
+ app_type_cfg->bit_width = s_info->app_type_cfg.bit_width;
ALOGV("%s app_type_cfg->app_type %d, app_type_cfg->sample_rate %d, app_type_cfg->bit_width %d",
__func__, app_type_cfg->app_type, app_type_cfg->sample_rate, app_type_cfg->bit_width);
return true;
@@ -469,13 +561,13 @@
*/
sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
- list_for_each(node_i, &so_info->sample_rate_list) {
+ list_for_each(node_i, &s_info->sample_rate_list) {
ss_info = node_to_item(node_i, struct stream_sample_rate, list);
if ((sample_rate <= ss_info->sample_rate) &&
- (bit_width == so_info->app_type_cfg.bit_width)) {
- app_type_cfg->app_type = so_info->app_type_cfg.app_type;
+ (bit_width == s_info->app_type_cfg.bit_width)) {
+ app_type_cfg->app_type = s_info->app_type_cfg.app_type;
app_type_cfg->sample_rate = sample_rate;
- app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
+ app_type_cfg->bit_width = s_info->app_type_cfg.bit_width;
ALOGV("%s Assuming sample rate. app_type_cfg->app_type %d, app_type_cfg->sample_rate %d, app_type_cfg->bit_width %d",
__func__, app_type_cfg->app_type, app_type_cfg->sample_rate, app_type_cfg->bit_width);
return true;
@@ -484,7 +576,45 @@
return false;
}
-void audio_extn_utils_update_stream_app_type_cfg(void *platform,
+void audio_extn_utils_update_stream_input_app_type_cfg(void *platform,
+ struct listnode *streams_input_cfg_list,
+ audio_devices_t devices __unused,
+ audio_input_flags_t flags,
+ audio_format_t format,
+ uint32_t sample_rate,
+ uint32_t bit_width,
+ char* profile,
+ struct stream_app_type_cfg *app_type_cfg)
+{
+ struct listnode *node_i, *node_j;
+ struct streams_io_cfg *s_info;
+ struct stream_format *sf_info;
+
+ ALOGV("%s: flags: 0x%x, format: 0x%x sample_rate %d, profile %s",
+ __func__, flags, format, sample_rate, profile);
+
+ list_for_each(node_i, streams_input_cfg_list) {
+ s_info = node_to_item(node_i, struct streams_io_cfg, list);
+ /* Along with flags do profile matching if set at either end.*/
+ if (s_info->flags.in_flags == flags &&
+ ((profile[0] == '\0' && s_info->profile[0] == '\0') ||
+ strncmp(s_info->profile, profile, sizeof(s_info->profile)) == 0)) {
+ list_for_each(node_j, &s_info->format_list) {
+ sf_info = node_to_item(node_j, struct stream_format, list);
+ if (sf_info->format == format) {
+ if (set_app_type_cfg(s_info, app_type_cfg, sample_rate, bit_width))
+ return;
+ }
+ }
+ }
+ }
+ ALOGW("%s: App type could not be selected. Falling back to default", __func__);
+ app_type_cfg->app_type = platform_get_default_app_type_v2(platform, PCM_CAPTURE);
+ app_type_cfg->sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ app_type_cfg->bit_width = 16;
+}
+
+void audio_extn_utils_update_stream_output_app_type_cfg(void *platform,
struct listnode *streams_output_cfg_list,
audio_devices_t devices,
audio_output_flags_t flags,
@@ -492,10 +622,11 @@
uint32_t sample_rate,
uint32_t bit_width,
audio_channel_mask_t channel_mask,
+ char *profile,
struct stream_app_type_cfg *app_type_cfg)
{
struct listnode *node_i, *node_j;
- struct streams_output_cfg *so_info;
+ struct streams_io_cfg *s_info;
struct stream_format *sf_info;
char value[PROPERTY_VALUE_MAX] = {0};
@@ -540,29 +671,32 @@
__func__, sample_rate, bit_width);
}
- ALOGV("%s: flags: %x, format: %x sample_rate %d",
- __func__, flags, format, sample_rate);
+ ALOGV("%s: flags: %x, format: %x sample_rate %d, profile %s",
+ __func__, flags, format, sample_rate, profile);
list_for_each(node_i, streams_output_cfg_list) {
- so_info = node_to_item(node_i, struct streams_output_cfg, list);
- if (so_info->flags == flags) {
- list_for_each(node_j, &so_info->format_list) {
+ s_info = node_to_item(node_i, struct streams_io_cfg, list);
+ /* Along with flags do profile matching if set at either end.*/
+ if (s_info->flags.out_flags == flags &&
+ ((profile[0] == '\0' && s_info->profile[0] == '\0') ||
+ strncmp(s_info->profile, profile, sizeof(s_info->profile)) == 0)) {
+ list_for_each(node_j, &s_info->format_list) {
sf_info = node_to_item(node_j, struct stream_format, list);
if (sf_info->format == format) {
- if (set_output_cfg(so_info, app_type_cfg, sample_rate, bit_width))
+ if (set_app_type_cfg(s_info, app_type_cfg, sample_rate, bit_width))
return;
}
}
}
}
list_for_each(node_i, streams_output_cfg_list) {
- so_info = node_to_item(node_i, struct streams_output_cfg, list);
- if (so_info->flags == AUDIO_OUTPUT_FLAG_PRIMARY) {
+ s_info = node_to_item(node_i, struct streams_io_cfg, list);
+ if (s_info->flags.out_flags == AUDIO_OUTPUT_FLAG_PRIMARY) {
ALOGV("Compatible output profile not found.");
- app_type_cfg->app_type = so_info->app_type_cfg.app_type;
- app_type_cfg->sample_rate = so_info->app_type_cfg.sample_rate;
- app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
+ app_type_cfg->app_type = s_info->app_type_cfg.app_type;
+ app_type_cfg->sample_rate = s_info->app_type_cfg.sample_rate;
+ app_type_cfg->bit_width = s_info->app_type_cfg.bit_width;
ALOGV("%s Default to primary output: App type: %d sample_rate %d",
- __func__, so_info->app_type_cfg.app_type, app_type_cfg->sample_rate);
+ __func__, s_info->app_type_cfg.app_type, app_type_cfg->sample_rate);
return;
}
}
@@ -586,20 +720,59 @@
return native_usecase;
}
+void audio_extn_utils_update_stream_app_type_cfg_for_usecase(
+ struct audio_device *adev,
+ struct audio_usecase *usecase)
+{
+ ALOGV("%s", __func__);
+
+ switch(usecase->type) {
+ case PCM_PLAYBACK:
+ audio_extn_utils_update_stream_output_app_type_cfg(adev->platform,
+ &adev->streams_output_cfg_list,
+ usecase->stream.out->devices,
+ usecase->stream.out->flags,
+ usecase->stream.out->format,
+ usecase->stream.out->sample_rate,
+ usecase->stream.out->bit_width,
+ usecase->stream.out->channel_mask,
+ usecase->stream.out->profile,
+ &usecase->stream.out->app_type_cfg);
+ ALOGV("%s Selected apptype: %d", __func__, usecase->stream.out->app_type_cfg.app_type);
+ break;
+ case PCM_CAPTURE:
+ audio_extn_utils_update_stream_input_app_type_cfg(adev->platform,
+ &adev->streams_input_cfg_list,
+ usecase->stream.in->device,
+ usecase->stream.in->flags,
+ usecase->stream.in->format,
+ usecase->stream.in->sample_rate,
+ usecase->stream.in->bit_width,
+ usecase->stream.in->profile,
+ &usecase->stream.in->app_type_cfg);
+ ALOGV("%s Selected apptype: %d", __func__, usecase->stream.in->app_type_cfg.app_type);
+ break;
+ default:
+ ALOGE("%s: app type cfg not supported for usecase type (%d)",
+ __func__, usecase->type);
+ }
+}
+
int audio_extn_utils_send_app_type_cfg(struct audio_device *adev,
struct audio_usecase *usecase)
{
char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
- int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc;
+ size_t app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
+ int len = 0, rc;
struct mixer_ctl *ctl;
- int pcm_device_id = 0, acdb_dev_id, snd_device = usecase->out_snd_device;
+ int pcm_device_id, acdb_dev_id = 0, snd_device = usecase->out_snd_device;
int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
char value[PROPERTY_VALUE_MAX] = {0};
ALOGV("%s", __func__);
if (usecase->type != PCM_PLAYBACK && usecase->type != PCM_CAPTURE) {
- ALOGE("%s: not a playback or capture path, no need to cfg app type", __func__);
+ ALOGE("%s: not a playback/capture path, no need to cfg app type", __func__);
rc = 0;
goto exit_send_app_type_cfg;
}
@@ -608,20 +781,28 @@
(usecase->id != USECASE_AUDIO_PLAYBACK_MULTI_CH) &&
(!is_offload_usecase(usecase->id)) &&
(usecase->type != PCM_CAPTURE)) {
- ALOGV("%s: a rx/tx path where app type cfg is not required %d", __func__, usecase->id);
+ ALOGV("%s: a rx/tx/loopback path where app type cfg is not required %d", __func__, usecase->id);
rc = 0;
goto exit_send_app_type_cfg;
}
if (usecase->type == PCM_PLAYBACK) {
snd_device = usecase->out_snd_device;
pcm_device_id = platform_get_pcm_device_id(usecase->id, PCM_PLAYBACK);
+ snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+ "Audio Stream %d App Type Cfg", pcm_device_id);
+ acdb_dev_id = platform_get_snd_device_acdb_id(usecase->out_snd_device);
} else if (usecase->type == PCM_CAPTURE) {
snd_device = usecase->in_snd_device;
pcm_device_id = platform_get_pcm_device_id(usecase->id, PCM_CAPTURE);
+ snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+ "Audio Stream Capture %d App Type Cfg", pcm_device_id);
+ acdb_dev_id = platform_get_snd_device_acdb_id(usecase->in_snd_device);
}
-
- snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
- "Audio Stream %d App Type Cfg", pcm_device_id);
+ if (acdb_dev_id <= 0) {
+ ALOGE("%s: Couldn't get the acdb dev id", __func__);
+ rc = -EINVAL;
+ goto exit_send_app_type_cfg;
+ }
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
if (!ctl) {
@@ -632,40 +813,25 @@
}
snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
platform_get_spkr_prot_snd_device(snd_device) : snd_device;
- acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
- if (acdb_dev_id < 0) {
- ALOGE("%s: Couldn't get the acdb dev id", __func__);
- rc = -EINVAL;
- goto exit_send_app_type_cfg;
- }
- if ((usecase->type == PCM_PLAYBACK) && (usecase->stream.out == NULL)) {
- sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
- app_type_cfg[len++] = platform_get_default_app_type(adev->platform);
- app_type_cfg[len++] = acdb_dev_id;
- app_type_cfg[len++] = sample_rate;
- ALOGI("%s:%d PLAYBACK app_type %d, acdb_dev_id %d, sample_rate %d",
- __func__, __LINE__,
- platform_get_default_app_type(adev->platform),
- acdb_dev_id, sample_rate);
- } else if (usecase->type == PCM_PLAYBACK) {
- if (usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
- usecase->stream.out->app_type_cfg.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
- } else if ((usecase->stream.out->app_type_cfg.sample_rate == OUTPUT_SAMPLING_RATE_44100 &&
- !(audio_is_this_native_usecase(usecase))) ||
- (usecase->stream.out->sample_rate < OUTPUT_SAMPLING_RATE_44100)) {
- usecase->stream.out->app_type_cfg.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
- }
+ if ((usecase->type == PCM_PLAYBACK) && (usecase->stream.out != NULL)) {
+ if (usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+ usecase->stream.out->app_type_cfg.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+ } else if ((usecase->stream.out->app_type_cfg.sample_rate == OUTPUT_SAMPLING_RATE_44100 &&
+ !(audio_is_this_native_usecase(usecase))) ||
+ (usecase->stream.out->sample_rate < OUTPUT_SAMPLING_RATE_44100)) {
+ usecase->stream.out->app_type_cfg.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+ }
- sample_rate = usecase->stream.out->app_type_cfg.sample_rate;
+ sample_rate = usecase->stream.out->app_type_cfg.sample_rate;
- property_get("audio.playback.mch.downsample",value,"");
- if (!strncmp("true", value, sizeof("true"))) {
- if ((popcount(usecase->stream.out->channel_mask) > 2) &&
- (usecase->stream.out->app_type_cfg.sample_rate > CODEC_BACKEND_DEFAULT_SAMPLE_RATE) &&
- !(usecase->stream.out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH))
- sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
- }
+ property_get("audio.playback.mch.downsample",value,"");
+ if (!strncmp("true", value, sizeof("true"))) {
+ if ((popcount(usecase->stream.out->channel_mask) > 2) &&
+ (usecase->stream.out->app_type_cfg.sample_rate > CODEC_BACKEND_DEFAULT_SAMPLE_RATE) &&
+ !(usecase->stream.out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH))
+ sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ }
if ((24 == usecase->stream.out->bit_width) &&
(usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER)) {
@@ -698,23 +864,28 @@
} else {
app_type_cfg[len++] = sample_rate;
}
- ALOGI("%s PLAYBACK app_type %d, acdb_dev_id %d, sample_rate %d",
- __func__, usecase->stream.out->app_type_cfg.app_type, acdb_dev_id, sample_rate);
- } else if (usecase->type == PCM_CAPTURE) {
- app_type_cfg[len++] = platform_get_default_app_type_v2(adev->platform, usecase->type);
- app_type_cfg[len++] = acdb_dev_id;
- app_type_cfg[len++] = sample_rate;
- ALOGI("%s CAPTURE app_type %d, acdb_dev_id %d, sample_rate %d",
- __func__, platform_get_default_app_type_v2(adev->platform, usecase->type),
- acdb_dev_id, sample_rate);
+
+ ALOGI("%s PLAYBACK app_type %d, acdb_dev_id %d, sample_rate %d",
+ __func__, usecase->stream.out->app_type_cfg.app_type, acdb_dev_id, sample_rate);
+
+ } else if ((usecase->type == PCM_CAPTURE) && (usecase->stream.in != NULL)) {
+ app_type_cfg[len++] = usecase->stream.in->app_type_cfg.app_type;
+ app_type_cfg[len++] = acdb_dev_id;
+ app_type_cfg[len++] = usecase->stream.in->app_type_cfg.sample_rate;
+ ALOGI("%s CAPTURE app_type %d, acdb_dev_id %d, sample_rate %d",
+ __func__, usecase->stream.in->app_type_cfg.app_type, acdb_dev_id,
+ usecase->stream.in->app_type_cfg.sample_rate);
+ } else {
+ app_type_cfg[len++] = platform_get_default_app_type_v2(adev->platform, usecase->type);
+ app_type_cfg[len++] = acdb_dev_id;
+ app_type_cfg[len++] = sample_rate;
+ ALOGI("%s default app_type %d, acdb_dev_id %d, sample_rate %d",
+ __func__, platform_get_default_app_type_v2(adev->platform, usecase->type),
+ acdb_dev_id, sample_rate);
}
mixer_ctl_set_array(ctl, app_type_cfg, len);
rc = 0;
- ALOGI("%s:becf: adm: app_type %d, acdb_dev_id %d, sample_rate %d",
- __func__,
- platform_get_default_app_type_v2(adev->platform, usecase->type),
- acdb_dev_id, sample_rate);
exit_send_app_type_cfg:
return rc;
}
@@ -912,6 +1083,66 @@
}
}
+/* converts pcm format 24_8 to 8_24 inplace */
+size_t audio_extn_utils_convert_format_24_8_to_8_24(void *buf, size_t bytes)
+{
+ size_t i = 0;
+ int *int_buf_stream = buf;
+
+ if ((bytes % 4) != 0) {
+ ALOGE("%s: wrong inout buffer! ... is not 32 bit aligned ", __func__);
+ return -EINVAL;
+ }
+
+ for (; i < (bytes / 4); i++)
+ int_buf_stream[i] >>= 8;
+
+ return bytes;
+}
+
+int get_snd_codec_id(audio_format_t format)
+{
+ int id = 0;
+
+ switch (format & AUDIO_FORMAT_MAIN_MASK) {
+ case AUDIO_FORMAT_MP3:
+ id = SND_AUDIOCODEC_MP3;
+ break;
+ case AUDIO_FORMAT_AAC:
+ id = SND_AUDIOCODEC_AAC;
+ break;
+ case AUDIO_FORMAT_AAC_ADTS:
+ id = SND_AUDIOCODEC_AAC;
+ break;
+ case AUDIO_FORMAT_PCM_OFFLOAD:
+ case AUDIO_FORMAT_PCM:
+ id = SND_AUDIOCODEC_PCM;
+ break;
+ case AUDIO_FORMAT_FLAC:
+ id = SND_AUDIOCODEC_FLAC;
+ break;
+ case AUDIO_FORMAT_ALAC:
+ id = SND_AUDIOCODEC_ALAC;
+ break;
+ case AUDIO_FORMAT_APE:
+ id = SND_AUDIOCODEC_APE;
+ break;
+ case AUDIO_FORMAT_VORBIS:
+ id = SND_AUDIOCODEC_VORBIS;
+ break;
+ case AUDIO_FORMAT_WMA:
+ id = SND_AUDIOCODEC_WMA;
+ break;
+ case AUDIO_FORMAT_WMA_PRO:
+ id = SND_AUDIOCODEC_WMA_PRO;
+ break;
+ default:
+ ALOGE("%s: Unsupported audio format :%x", __func__, format);
+ }
+
+ return id;
+}
+
void audio_extn_utils_send_audio_calibration(struct audio_device *adev,
struct audio_usecase *usecase)
{
@@ -925,12 +1156,15 @@
platform_send_audio_calibration(adev->platform, usecase,
out->app_type_cfg.app_type,
usecase->stream.out->app_type_cfg.sample_rate);
- }
- if ((type == PCM_HFP_CALL) || (type == PCM_CAPTURE)) {
+ } else if (type == PCM_CAPTURE) {
+ platform_send_audio_calibration(adev->platform, usecase,
+ usecase->stream.in->app_type_cfg.app_type,
+ usecase->stream.in->app_type_cfg.sample_rate);
+ } else {
/* when app type is default. the sample rate is not used to send cal */
platform_send_audio_calibration(adev->platform, usecase,
- platform_get_default_app_type_v2(adev->platform, usecase->type),
- 48000);
+ platform_get_default_app_type_v2(adev->platform, usecase->type),
+ 48000);
}
}
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index bd393f2..fb668d5 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -227,6 +227,9 @@
[USECASE_AUDIO_RECORD] = "audio-record",
[USECASE_AUDIO_RECORD_COMPRESS] = "audio-record-compress",
+ [USECASE_AUDIO_RECORD_COMPRESS2] = "audio-record-compress2",
+ [USECASE_AUDIO_RECORD_COMPRESS3] = "audio-record-compress3",
+ [USECASE_AUDIO_RECORD_COMPRESS4] = "audio-record-compress4",
[USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
[USECASE_AUDIO_RECORD_FM_VIRTUAL] = "fm-virtual-record",
[USECASE_AUDIO_PLAYBACK_FM] = "play-fm",
@@ -505,62 +508,6 @@
(uc_id == USECASE_AUDIO_PLAYBACK_AFE_PROXY);
}
-static int get_snd_codec_id(audio_format_t format)
-{
- int id = 0;
-
- switch (format & AUDIO_FORMAT_MAIN_MASK) {
- case AUDIO_FORMAT_MP3:
- id = SND_AUDIOCODEC_MP3;
- break;
- case AUDIO_FORMAT_AAC:
- id = SND_AUDIOCODEC_AAC;
- break;
- case AUDIO_FORMAT_AAC_ADTS:
- id = SND_AUDIOCODEC_AAC;
- break;
- case AUDIO_FORMAT_PCM:
- id = SND_AUDIOCODEC_PCM;
- break;
- case AUDIO_FORMAT_FLAC:
- id = SND_AUDIOCODEC_FLAC;
- break;
- case AUDIO_FORMAT_ALAC:
- id = SND_AUDIOCODEC_ALAC;
- break;
- case AUDIO_FORMAT_APE:
- id = SND_AUDIOCODEC_APE;
- break;
- case AUDIO_FORMAT_DSD:
- id = SND_AUDIOCODEC_DSD;
- break;
- case AUDIO_FORMAT_VORBIS:
- id = SND_AUDIOCODEC_VORBIS;
- break;
- case AUDIO_FORMAT_WMA:
- id = SND_AUDIOCODEC_WMA;
- break;
- case AUDIO_FORMAT_WMA_PRO:
- id = SND_AUDIOCODEC_WMA_PRO;
- break;
- case AUDIO_FORMAT_AC3:
- id = SND_AUDIOCODEC_AC3;
- break;
- case AUDIO_FORMAT_E_AC3:
- case AUDIO_FORMAT_E_AC3_JOC:
- id = SND_AUDIOCODEC_EAC3;
- break;
- case AUDIO_FORMAT_DTS:
- case AUDIO_FORMAT_DTS_HD:
- id = SND_AUDIOCODEC_DTS;
- break;
- default:
- ALOGE("%s: Unsupported audio format :%x", __func__, format);
- }
-
- return id;
-}
-
int get_snd_card_state(struct audio_device *adev)
{
int snd_scard_state;
@@ -1142,10 +1089,19 @@
struct audio_usecase *usecase;
bool switch_device[AUDIO_USECASE_MAX];
int i, num_uc_to_switch = 0;
+ int backend_check_cond = AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND;
bool force_routing = platform_check_and_set_capture_codec_backend_cfg(adev, uc_info,
snd_device);
ALOGD("%s:becf: force routing %d", __func__, force_routing);
+
+ /*
+ * Make sure out devices is checked against out codec backend device and
+ * also in devices against in codec backend. Checking out device against in
+ * codec backend or vice versa causes issues.
+ */
+ if (uc_info->type == PCM_CAPTURE)
+ backend_check_cond = AUDIO_DEVICE_IN_ALL_CODEC_BACKEND;
/*
* This function is to make sure that all the active capture usecases
* are always routed to the same input sound device.
@@ -1161,10 +1117,13 @@
list_for_each(node, &adev->usecase_list) {
usecase = node_to_item(node, struct audio_usecase, list);
+ /*
+ * TODO: Enhance below condition to handle BT sco/USB multi recording
+ */
if (usecase->type != PCM_PLAYBACK &&
usecase != uc_info &&
(usecase->in_snd_device != snd_device || force_routing) &&
- ((uc_info->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) &&
+ ((uc_info->devices & backend_check_cond) &&
(((usecase->devices & ~AUDIO_DEVICE_BIT_IN) & AUDIO_DEVICE_IN_ALL_CODEC_BACKEND) ||
(usecase->type == VOICE_CALL) || (usecase->type == VOIP_CALL))) &&
(usecase->id != USECASE_AUDIO_SPKR_CALIB_TX)) {
@@ -1616,25 +1575,15 @@
usecase->in_snd_device = in_snd_device;
usecase->out_snd_device = out_snd_device;
+ audio_extn_utils_update_stream_app_type_cfg_for_usecase(adev,
+ usecase);
if (usecase->type == PCM_PLAYBACK) {
- audio_extn_utils_update_stream_app_type_cfg(adev->platform,
- &adev->streams_output_cfg_list,
- usecase->stream.out->devices,
- usecase->stream.out->flags,
- usecase->stream.out->format,
- usecase->stream.out->sample_rate,
- usecase->stream.out->bit_width,
- usecase->stream.out->channel_mask,
- &usecase->stream.out->app_type_cfg);
- ALOGI("%s Selected apptype: %d", __func__, usecase->stream.out->app_type_cfg.app_type);
-
/* Notify device change info to effect clients registered */
audio_extn_gef_notify_device_config(
usecase->stream.out->devices,
usecase->stream.out->channel_mask,
platform_get_snd_device_acdb_id(usecase->out_snd_device));
}
-
enable_audio_route(adev, usecase);
if (usecase->type == VOICE_CALL || usecase->type == VOIP_CALL) {
@@ -1760,6 +1709,14 @@
ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d format %d",
__func__, adev->snd_card, in->pcm_device_id, in->config.channels, in->config.format);
+ if (audio_extn_cin_attached_usecase(in->usecase)) {
+ ret = audio_extn_cin_start_input_stream(in);
+ if (ret)
+ goto error_open;
+ else
+ goto done_open;
+ }
+
unsigned int flags = PCM_IN;
unsigned int pcm_open_retry_count = 0;
@@ -1805,6 +1762,7 @@
goto error_open;
}
+done_open:
audio_extn_perf_lock_release(&adev->perf_lock_handle);
ALOGD("%s: exit", __func__);
@@ -2681,6 +2639,20 @@
pthread_mutex_unlock(&out->lock);
}
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_PROFILE, value, sizeof(value));
+ if (err >= 0) {
+ strlcpy(out->profile, value, sizeof(out->profile));
+ ALOGV("updating stream profile with value '%s'", out->profile);
+ lock_output_stream(out);
+ audio_extn_utils_update_stream_output_app_type_cfg(adev->platform,
+ &adev->streams_output_cfg_list,
+ out->devices, out->flags, out->format,
+ out->sample_rate, out->bit_width,
+ out->channel_mask, out->profile,
+ &out->app_type_cfg);
+ pthread_mutex_unlock(&out->lock);
+ }
+
str_parms_destroy(parms);
error:
ALOGV("%s: exit: code(%d)", __func__, ret);
@@ -3396,6 +3368,8 @@
return voice_extn_compress_voip_in_get_buffer_size(in);
else if(audio_extn_compr_cap_usecase_supported(in->usecase))
return audio_extn_compr_cap_get_buffer_size(in->config.format);
+ else if(audio_extn_cin_attached_usecase(in->usecase))
+ return audio_extn_cin_get_buffer_size(in);
return in->config.period_size * in->af_period_multiplier *
audio_stream_in_frame_size((const struct audio_stream_in *)stream);
@@ -3446,6 +3420,8 @@
voice_extn_compress_voip_close_input_stream(stream);
ALOGD("VOIP input entered standby");
} else {
+ if (audio_extn_cin_attached_usecase(in->usecase))
+ audio_extn_cin_stop_input_stream(in);
if (in->pcm) {
pcm_close(in->pcm);
in->pcm = NULL;
@@ -3518,6 +3494,17 @@
}
}
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_PROFILE, value, sizeof(value));
+ if (err >= 0) {
+ strlcpy(in->profile, value, sizeof(in->profile));
+ ALOGV("updating stream profile with value '%s'", in->profile);
+ audio_extn_utils_update_stream_input_app_type_cfg(adev->platform,
+ &adev->streams_input_cfg_list,
+ in->device, in->flags, in->format,
+ in->sample_rate, in->bit_width,
+ in->profile, &in->app_type_cfg);
+ }
+
pthread_mutex_unlock(&adev->lock);
pthread_mutex_unlock(&in->lock);
@@ -3571,7 +3558,7 @@
struct audio_device *adev = in->dev;
int ret = -1;
int snd_scard_state = get_snd_card_state(adev);
- int *int_buf_stream = NULL;
+ size_t bytes_read = 0;
lock_input_stream(in);
@@ -3605,14 +3592,16 @@
// what's the duration requested by the client?
long ns = 0;
- if (in->config.rate)
+ if (in->pcm && in->config.rate)
ns = pcm_bytes_to_frames(in->pcm, bytes)*1000000000LL/
in->config.rate;
request_in_focus(in, ns);
bool use_mmap = is_mmap_usecase(in->usecase) || in->realtime;
- if (in->pcm) {
+ if (audio_extn_cin_attached_usecase(in->usecase)) {
+ ret = audio_extn_cin_read(in, buffer, bytes, &bytes_read);
+ } else if (in->pcm) {
if (audio_extn_ssr_get_stream() == in) {
ret = audio_extn_ssr_read(stream, buffer, bytes);
} else if (audio_extn_compr_cap_usecase_supported(in->usecase)) {
@@ -3621,23 +3610,19 @@
ret = pcm_mmap_read(in->pcm, buffer, bytes);
} else {
ret = pcm_read(in->pcm, buffer, bytes);
- if ( !ret && bytes > 0 && (in->format == AUDIO_FORMAT_PCM_8_24_BIT)) {
- if (bytes % 4 == 0) {
- /* data from DSP comes in 24_8 format, convert it to 8_24 */
- int_buf_stream = buffer;
- size_t itt = 0;
- for (itt = 0; itt < bytes/4 ; itt++) {
- int_buf_stream[itt] >>= 8;
- }
- } else {
- ALOGE("%s: !!! something wrong !!! ... data not 32 bit aligned ", __func__);
+ /* data from DSP comes in 24_8 format, convert it to 8_24 */
+ if (!ret && bytes > 0 && (in->format == AUDIO_FORMAT_PCM_8_24_BIT)) {
+ if (audio_extn_utils_convert_format_24_8_to_8_24(buffer, bytes)
+ != bytes) {
ret = -EINVAL;
goto exit;
}
- } if (ret < 0) {
+ } else if (ret < 0) {
ret = -errno;
}
}
+ /* bytes read is always set to bytes for non compress usecases */
+ bytes_read = bytes;
}
release_in_focus(in);
@@ -3671,7 +3656,7 @@
usleep((uint64_t)bytes * 1000000 / audio_stream_in_frame_size(stream) /
in_get_sample_rate(&in->stream.common));
}
- return bytes;
+ return bytes_read;
}
static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream __unused)
@@ -4137,10 +4122,10 @@
/* TODO remove this hardcoding and check why width is zero*/
if (out->bit_width == 0)
out->bit_width = 16;
- audio_extn_utils_update_stream_app_type_cfg(adev->platform,
+ audio_extn_utils_update_stream_output_app_type_cfg(adev->platform,
&adev->streams_output_cfg_list,
devices, flags, format, out->sample_rate,
- out->bit_width, out->channel_mask,
+ out->bit_width, out->channel_mask, out->profile,
&out->app_type_cfg);
if ((out->usecase == USECASE_AUDIO_PLAYBACK_PRIMARY) ||
(flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
@@ -4591,7 +4576,7 @@
audio_devices_t devices,
struct audio_config *config,
struct audio_stream_in **stream_in,
- audio_input_flags_t flags __unused,
+ audio_input_flags_t flags,
const char *address __unused,
audio_source_t source)
{
@@ -4645,17 +4630,6 @@
in->capture_handle = handle;
in->flags = flags;
- /* Update config params with the requested sample rate and channels */
- in->usecase = USECASE_AUDIO_RECORD;
- if (config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE &&
- (flags & AUDIO_INPUT_FLAG_FAST) != 0) {
- is_low_latency = true;
-#if LOW_LATENCY_CAPTURE_USE_CASE
- in->usecase = USECASE_AUDIO_RECORD_LOW_LATENCY;
-#endif
- in->realtime = may_use_noirq_mode(adev, in->usecase, in->flags);
- }
-
in->format = config->format;
if (in->realtime) {
in->config = pcm_config_audio_capture_rt;
@@ -4669,6 +4643,75 @@
}
in->bit_width = 16;
+ /* restrict 24 bit capture for unprocessed source only
+ * for other sources if 24 bit requested reject 24 and set 16 bit capture only
+ */
+ if (config->format == AUDIO_FORMAT_DEFAULT) {
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+ } else if ((config->format == AUDIO_FORMAT_PCM_FLOAT) ||
+ (config->format == AUDIO_FORMAT_PCM_32_BIT) ||
+ (config->format == AUDIO_FORMAT_PCM_24_BIT_PACKED) ||
+ (config->format == AUDIO_FORMAT_PCM_8_24_BIT)) {
+ bool ret_error = false;
+ in->bit_width = 24;
+ /* 24 bit is restricted to UNPROCESSED source only,also format supported
+ from HAL is 24_packed and 8_24
+ *> In case of UNPROCESSED source, for 24 bit, if format requested is other than
+ 24_packed return error indicating supported format is 24_packed
+ *> In case of any other source requesting 24 bit or float return error
+ indicating format supported is 16 bit only.
+
+ on error flinger will retry with supported format passed
+ */
+ if ((source != AUDIO_SOURCE_UNPROCESSED) &&
+ (source != AUDIO_SOURCE_CAMCORDER)) {
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+ if (config->sample_rate > 48000)
+ config->sample_rate = 48000;
+ ret_error = true;
+ } else if (config->format == AUDIO_FORMAT_PCM_24_BIT_PACKED) {
+ in->config.format = PCM_FORMAT_S24_3LE;
+ } else if (config->format == AUDIO_FORMAT_PCM_8_24_BIT) {
+ in->config.format = PCM_FORMAT_S24_LE;
+ } else {
+ config->format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+ ret_error = true;
+ }
+
+ if (ret_error) {
+ ret = -EINVAL;
+ goto err_open;
+ }
+ }
+
+ in->usecase = USECASE_AUDIO_RECORD;
+ if (config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE &&
+ (flags & AUDIO_INPUT_FLAG_FAST) != 0) {
+ is_low_latency = true;
+#if LOW_LATENCY_CAPTURE_USE_CASE
+ in->usecase = USECASE_AUDIO_RECORD_LOW_LATENCY;
+#endif
+ in->realtime = may_use_noirq_mode(adev, in->usecase, in->flags);
+ }
+
+ pthread_mutex_lock(&adev->lock);
+ if (in->usecase == USECASE_AUDIO_RECORD) {
+ if (!(adev->pcm_record_uc_state) &&
+ ((flags & AUDIO_INPUT_FLAG_TIMESTAMP) == 0)) {
+ ALOGV("%s: pcm record usecase", __func__);
+ adev->pcm_record_uc_state = 1;
+ } else {
+ /*
+ * Assign default compress record use case, actual use case
+ * assignment will happen later.
+ */
+ in->usecase = USECASE_AUDIO_RECORD_COMPRESS2;
+ ALOGV("%s: compress record usecase", __func__);
+ }
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ /* Update config params with the requested sample rate and channels */
if (in->device == AUDIO_DEVICE_IN_TELEPHONY_RX) {
if (adev->mode != AUDIO_MODE_IN_CALL) {
ret = -EINVAL;
@@ -4708,48 +4751,11 @@
audio_extn_compr_cap_format_supported(config->format) &&
(in->dev->mode != AUDIO_MODE_IN_COMMUNICATION)) {
audio_extn_compr_cap_init(in);
+ } else if (audio_extn_cin_applicable_stream(in)) {
+ ret = audio_extn_cin_configure_input_stream(in);
+ if (ret)
+ goto err_open;
} else {
- /* restrict 24 bit capture for unprocessed source only
- * for other sources if 24 bit requested reject 24 and set 16 bit capture only
- */
- if (config->format == AUDIO_FORMAT_DEFAULT) {
- config->format = AUDIO_FORMAT_PCM_16_BIT;
- } else if ((config->format == AUDIO_FORMAT_PCM_FLOAT) ||
- (config->format == AUDIO_FORMAT_PCM_32_BIT) ||
- (config->format == AUDIO_FORMAT_PCM_24_BIT_PACKED) ||
- (config->format == AUDIO_FORMAT_PCM_8_24_BIT)) {
- bool ret_error = false;
- in->bit_width = 24;
- /* 24 bit is restricted to UNPROCESSED source only,also format supported
- from HAL is 24_packed and 8_24
- *> In case of UNPROCESSED source, for 24 bit, if format requested is other than
- 24_packed return error indicating supported format is 24_packed
- *> In case of any other source requesting 24 bit or float return error
- indicating format supported is 16 bit only.
-
- on error flinger will retry with supported format passed
- */
- if ((source != AUDIO_SOURCE_UNPROCESSED) &&
- (source != AUDIO_SOURCE_CAMCORDER)) {
- config->format = AUDIO_FORMAT_PCM_16_BIT;
- if( config->sample_rate > 48000)
- config->sample_rate = 48000;
- ret_error = true;
- } else if (config->format == AUDIO_FORMAT_PCM_24_BIT_PACKED) {
- in->config.format = PCM_FORMAT_S24_3LE;
- } else if (config->format == AUDIO_FORMAT_PCM_8_24_BIT) {
- in->config.format = PCM_FORMAT_S24_LE;
- } else {
- config->format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
- ret_error = true;
- }
-
- if (ret_error) {
- ret = -EINVAL;
- goto err_open;
- }
- }
-
in->config.channels = channel_count;
if (!in->realtime) {
in->format = config->format;
@@ -4771,6 +4777,11 @@
}
}
+ audio_extn_utils_update_stream_input_app_type_cfg(adev->platform,
+ &adev->streams_input_cfg_list,
+ devices, flags, in->format, in->sample_rate,
+ in->bit_width, in->profile, &in->app_type_cfg);
+
/* This stream could be for sound trigger lab,
get sound trigger pcm if present */
audio_extn_sound_trigger_check_and_get_session(in);
@@ -4811,10 +4822,19 @@
audio_extn_ssr_deinit();
}
- if(audio_extn_compr_cap_enabled() &&
+ if (audio_extn_compr_cap_enabled() &&
audio_extn_compr_cap_format_supported(in->config.format))
audio_extn_compr_cap_deinit();
+ if (in->usecase == USECASE_AUDIO_RECORD) {
+ pthread_mutex_lock(&adev->lock);
+ adev->pcm_record_uc_state = 0;
+ pthread_mutex_unlock(&adev->lock);
+ }
+
+ if (audio_extn_cin_attached_usecase(in->usecase))
+ audio_extn_cin_close_input_stream(in);
+
if (in->is_st_session) {
ALOGV("%s: sound trigger pcm stop lab", __func__);
audio_extn_sound_trigger_stop_lab(in);
@@ -4841,15 +4861,18 @@
if ((--audio_device_ref_count) == 0) {
audio_extn_sound_trigger_deinit(adev);
audio_extn_listen_deinit(adev);
+ audio_extn_utils_release_streams_cfg_lists(
+ &adev->streams_output_cfg_list,
+ &adev->streams_input_cfg_list);
if (audio_extn_qaf_is_enabled())
audio_extn_qaf_deinit();
- audio_extn_utils_release_streams_output_cfg_list(&adev->streams_output_cfg_list);
audio_route_free(adev->audio_route);
audio_extn_gef_deinit();
free(adev->snd_dev_ref_cnt);
platform_deinit(adev->platform);
if (adev->adm_deinit)
adev->adm_deinit(adev->adm_data);
+ qahwi_deinit(device);
free(device);
adev = NULL;
}
@@ -4940,6 +4963,7 @@
list_init(&adev->usecase_list);
adev->cur_wfd_channels = 2;
adev->offload_usecases_state = 0;
+ adev->pcm_record_uc_state = 0;
adev->is_channel_status_set = false;
adev->perf_lock_opts[0] = 0x101;
adev->perf_lock_opts[1] = 0x20E;
@@ -5058,8 +5082,9 @@
audio_extn_ds2_enable(adev);
*device = &adev->device.common;
- audio_extn_utils_update_streams_output_cfg_list(adev->platform, adev->mixer,
- &adev->streams_output_cfg_list);
+ audio_extn_utils_update_streams_cfg_lists(adev->platform, adev->mixer,
+ &adev->streams_output_cfg_list,
+ &adev->streams_input_cfg_list);
audio_device_ref_count++;
@@ -5097,6 +5122,7 @@
if (adev->adm_init)
adev->adm_data = adev->adm_init();
+ qahwi_init(*device);
audio_extn_perf_lock_init();
ALOGV("%s: exit", __func__);
return 0;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 83ad54d..f837952 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -47,6 +47,7 @@
#include <audio_route/audio_route.h>
#include "audio_defs.h"
#include "voice.h"
+#include "audio_hw_extn_api.h"
#define VISUALIZER_LIBRARY_PATH "/system/lib/soundfx/libqcomvisualizer.so"
#define OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH "/system/lib/soundfx/libqcompostprocbundle.so"
@@ -79,6 +80,8 @@
#define MAX_PERF_LOCK_OPTS 20
+#define MAX_STREAM_PROFILE_STR_LEN 32
+
typedef enum card_status_t {
CARD_STATUS_OFFLINE,
CARD_STATUS_ONLINE
@@ -115,6 +118,9 @@
/* Capture usecases */
USECASE_AUDIO_RECORD,
USECASE_AUDIO_RECORD_COMPRESS,
+ USECASE_AUDIO_RECORD_COMPRESS2,
+ USECASE_AUDIO_RECORD_COMPRESS3,
+ USECASE_AUDIO_RECORD_COMPRESS4,
USECASE_AUDIO_RECORD_LOW_LATENCY,
USECASE_AUDIO_RECORD_FM_VIRTUAL,
@@ -206,6 +212,7 @@
audio_format_t format;
audio_devices_t devices;
audio_output_flags_t flags;
+ char profile[MAX_STREAM_PROFILE_STR_LEN];
audio_usecase_t usecase;
/* Array of supported channel mask configurations. +1 so that the last entry is always 0 */
audio_channel_mask_t supported_channel_masks[MAX_SUPPORTED_CHANNEL_MASKS + 1];
@@ -262,12 +269,17 @@
audio_format_t format;
audio_io_handle_t capture_handle;
audio_input_flags_t flags;
+ char profile[MAX_STREAM_PROFILE_STR_LEN];
bool is_st_session;
bool is_st_session_active;
int sample_rate;
int bit_width;
bool realtime;
int af_period_multiplier;
+ struct stream_app_type_cfg app_type_cfg;
+ void *cin_extn;
+ qahwi_stream_in_t qahwi_in;
+
struct audio_device *dev;
};
@@ -291,6 +303,8 @@
audio_devices_t devices;
snd_device_t out_snd_device;
snd_device_t in_snd_device;
+ struct stream_app_type_cfg out_app_type_cfg;
+ struct stream_app_type_cfg in_app_type_cfg;
union stream_ptr stream;
};
@@ -309,9 +323,15 @@
uint32_t sample_rate;
};
-struct streams_output_cfg {
+typedef union {
+ audio_output_flags_t out_flags;
+ audio_input_flags_t in_flags;
+} audio_io_flags_t;
+
+struct streams_io_cfg {
struct listnode list;
- audio_output_flags_t flags;
+ audio_io_flags_t flags;
+ char profile[MAX_STREAM_PROFILE_STR_LEN];
struct listnode format_list;
struct listnode sample_rate_list;
struct stream_app_type_cfg app_type_cfg;
@@ -346,6 +366,7 @@
int *snd_dev_ref_cnt;
struct listnode usecase_list;
struct listnode streams_output_cfg_list;
+ struct listnode streams_input_cfg_list;
struct audio_route *audio_route;
int acdb_settings;
bool speaker_lr_swap;
@@ -364,6 +385,7 @@
bool is_channel_status_set;
void *platform;
unsigned int offload_usecases_state;
+ unsigned int pcm_record_uc_state;
void *visualizer_lib;
int (*visualizer_start_output)(audio_io_handle_t, int);
int (*visualizer_stop_output)(audio_io_handle_t, int);
@@ -398,6 +420,7 @@
int perf_lock_opts_size;
bool native_playback_enabled;
bool asrc_mode_enabled;
+ qahwi_device_t qahwi_dev;
};
int select_devices(struct audio_device *adev,
diff --git a/hal/audio_hw_extn_api.c b/hal/audio_hw_extn_api.c
new file mode 100644
index 0000000..c4a3cdf
--- /dev/null
+++ b/hal/audio_hw_extn_api.c
@@ -0,0 +1,220 @@
+/*
+* Copyright (c) 2016, 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 <errno.h>
+#include <cutils/log.h>
+
+#include <hardware/audio.h>
+#include "sound/compress_params.h"
+#include "audio_hw.h"
+#include "audio_extn.h"
+#include "audio_hw_extn_api.h"
+
+/* 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
+
+int qahwi_get_param_data(const struct audio_device *adev,
+ qahw_param_id param_id, qahw_param_payload *payload)
+{
+ int ret = 0;
+
+ if (adev == NULL) {
+ ALOGE("%s::INVALID PARAM\n",__func__);
+ return -EINVAL;
+ }
+
+ if (payload == NULL) {
+ ALOGE("%s::INVALID PAYLOAD VALUE\n",__func__);
+ return -EINVAL;
+ }
+
+ switch (param_id) {
+ case QAHW_PARAM_SOUND_FOCUS:
+ ret = audio_extn_get_soundfocus_data(adev,
+ (struct qahw_sound_focus_param *)payload);
+ break;
+ case QAHW_PARAM_SOURCE_TRACK:
+ ret = audio_extn_get_sourcetrack_data(adev,
+ (struct qahw_source_tracking_param*)payload);
+ break;
+ default:
+ ALOGE("%s::INVALID PARAM ID:%d\n",__func__,param_id);
+ ret = -EINVAL;
+ break;
+
+ return ret;
+ }
+}
+
+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)) {
+ 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) {
+ bytes_read = bytes;
+ memcpy(buffer, buf + mdata_size, bytes);
+ if (timestamp) {
+ mdata = (struct snd_codec_metadata *) buf;
+ *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 &&
+ (flags & AUDIO_INPUT_FLAG_TIMESTAMP)) {
+ // 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;
+}
+
+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->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__);
+ }
+}
+
diff --git a/hal/audio_hw_extn_api.h b/hal/audio_hw_extn_api.h
new file mode 100644
index 0000000..e5fa9ec
--- /dev/null
+++ b/hal/audio_hw_extn_api.h
@@ -0,0 +1,60 @@
+/*
+* Copyright (c) 2016, 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.
+*/
+
+#ifndef AUDIO_HW_EXTN_API_H
+#define AUDIO_HW_EXTN_API_H
+__BEGIN_DECLS
+#ifdef AUDIO_HW_EXTN_API_ENABLED
+#include <hardware/audio.h>
+typedef struct qahwi_stream_in qahwi_stream_in_t;
+typedef struct qahwi_device qahwi_device_t;
+
+struct qahwi_stream_in {
+ struct audio_stream_in base;
+ bool is_inititalized;
+ void *ibuf;
+};
+
+struct qahwi_device {
+ struct audio_hw_device base;
+ bool is_inititalized;
+};
+
+void qahwi_init(hw_device_t *device);
+void qahwi_deinit(hw_device_t *device);
+#else
+typedef void *qahwi_stream_in_t;
+typedef void *qahwi_device_t;
+
+#define qahwi_init(device) (0)
+#define qahwi_deinit(device) (0)
+#endif // AUDIO_HW_EXTN_API_ENABLED
+
+__END_DECLS
+#endif // AUDIO_HW_EXTN_API_H
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 012638c..c2bcbe0 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -301,6 +301,9 @@
[USECASE_AUDIO_PLAYBACK_ULL] = {MULTIMEDIA3_PCM_DEVICE, MULTIMEDIA3_PCM_DEVICE},
[USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE, AUDIO_RECORD_PCM_DEVICE},
[USECASE_AUDIO_RECORD_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, COMPRESS_CAPTURE_DEVICE},
+ [USECASE_AUDIO_RECORD_COMPRESS2] = {-1, -1},
+ [USECASE_AUDIO_RECORD_COMPRESS3] = {-1, -1},
+ [USECASE_AUDIO_RECORD_COMPRESS4] = {-1, -1},
[USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
LOWLATENCY_PCM_DEVICE},
[USECASE_AUDIO_RECORD_FM_VIRTUAL] = {MULTIMEDIA2_PCM_DEVICE,
@@ -753,6 +756,10 @@
{TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD9)},
{TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_ULL)},
{TO_NAME_INDEX(USECASE_AUDIO_RECORD)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_COMPRESS)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_COMPRESS2)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_COMPRESS3)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_COMPRESS4)},
{TO_NAME_INDEX(USECASE_AUDIO_RECORD_LOW_LATENCY)},
{TO_NAME_INDEX(USECASE_VOICE_CALL)},
{TO_NAME_INDEX(USECASE_VOICE2_CALL)},
@@ -2600,6 +2607,8 @@
if (backend_tag_table[snd_device] != NULL) {
if (strcmp(backend_tag_table[snd_device], "usb-headset-mic") == 0)
port = USB_AUDIO_TX_BACKEND;
+ else if (strstr(backend_tag_table[snd_device], "bt-sco") != NULL)
+ port = BT_SCO_TX_BACKEND;
}
} else {
ALOGW("%s:napb: Invalid device - %d ", __func__, snd_device);
@@ -3317,6 +3326,10 @@
{
struct platform_data *my_data = (struct platform_data *)platform;
struct audio_device *adev = my_data->adev;
+ /*
+ * TODO: active_input always points to last opened input. Source returned will
+ * be wrong if more than one active inputs are present.
+ */
audio_source_t source = (adev->active_input == NULL) ?
AUDIO_SOURCE_DEFAULT : adev->active_input->source;
@@ -4370,8 +4383,8 @@
__func__, bit_width, sample_rate, channels,backend_idx,
platform_get_snd_device_name(snd_device));
- if (bit_width !=
- my_data->current_backend_cfg[backend_idx].bit_width) {
+ if ((my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl) &&
+ (bit_width != my_data->current_backend_cfg[backend_idx].bit_width)) {
struct mixer_ctl *ctl = NULL;
ctl = mixer_get_ctl_by_name(adev->mixer,
@@ -4409,7 +4422,8 @@
*/
// TODO: This has to be more dynamic based on policy file
- if ((sample_rate != my_data->current_backend_cfg[(int)backend_idx].sample_rate) &&
+ if ((my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl) &&
+ (sample_rate != my_data->current_backend_cfg[(int)backend_idx].sample_rate) &&
(my_data->hifi_audio)) {
/*
* sample rate update is needed only for hifi audio enabled platforms
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index a0ae7d0..24c793e 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -240,6 +240,7 @@
SLIMBUS_0_TX,
DEFAULT_CODEC_TX_BACKEND = SLIMBUS_0_TX,
USB_AUDIO_TX_BACKEND,
+ BT_SCO_TX_BACKEND,
MAX_CODEC_BACKENDS
};
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 3abb904..81741fc 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -286,6 +286,9 @@
[USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE, AUDIO_RECORD_PCM_DEVICE},
[USECASE_AUDIO_RECORD_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, COMPRESS_CAPTURE_DEVICE},
+ [USECASE_AUDIO_RECORD_COMPRESS2] = {-1, -1},
+ [USECASE_AUDIO_RECORD_COMPRESS3] = {-1, -1},
+ [USECASE_AUDIO_RECORD_COMPRESS4] = {-1, -1},
[USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
LOWLATENCY_PCM_DEVICE},
[USECASE_AUDIO_RECORD_FM_VIRTUAL] = {MULTIMEDIA2_PCM_DEVICE,
@@ -721,6 +724,10 @@
{TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD8)},
{TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD9)},
{TO_NAME_INDEX(USECASE_AUDIO_RECORD)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_COMPRESS)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_COMPRESS2)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_COMPRESS3)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_COMPRESS4)},
{TO_NAME_INDEX(USECASE_AUDIO_RECORD_LOW_LATENCY)},
{TO_NAME_INDEX(USECASE_VOICE_CALL)},
{TO_NAME_INDEX(USECASE_VOICE2_CALL)},
@@ -2363,6 +2370,8 @@
if (backend_tag_table[snd_device] != NULL) {
if (strcmp(backend_tag_table[snd_device], "usb-headset-mic") == 0)
port = USB_AUDIO_TX_BACKEND;
+ else if (strstr(backend_tag_table[snd_device], "bt-sco") != NULL)
+ port = BT_SCO_TX_BACKEND;
}
} else {
ALOGW("%s:napb: Invalid device - %d ", __func__, snd_device);
@@ -3018,6 +3027,10 @@
{
struct platform_data *my_data = (struct platform_data *)platform;
struct audio_device *adev = my_data->adev;
+ /*
+ * TODO: active_input always points to last opened input. Source returned will
+ * be wrong if more than one active inputs are present.
+ */
audio_source_t source = (adev->active_input == NULL) ?
AUDIO_SOURCE_DEFAULT : adev->active_input->source;
@@ -4350,8 +4363,8 @@
sample_rate, channels, backend_idx,
platform_get_snd_device_name(snd_device));
- if (bit_width !=
- my_data->current_backend_cfg[backend_idx].bit_width) {
+ if ((my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl) &&
+ (bit_width != my_data->current_backend_cfg[backend_idx].bit_width)) {
struct mixer_ctl *ctl = NULL;
ctl = mixer_get_ctl_by_name(adev->mixer,
@@ -4385,8 +4398,8 @@
ret = 0;
}
- if (sample_rate !=
- my_data->current_backend_cfg[backend_idx].sample_rate) {
+ if ((my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl) &&
+ (sample_rate != my_data->current_backend_cfg[backend_idx].sample_rate)) {
char *rate_str = NULL;
struct mixer_ctl *ctl = NULL;
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 6f55c2c..eb34293 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -231,6 +231,7 @@
SLIMBUS_0_TX,
DEFAULT_CODEC_TX_BACKEND = SLIMBUS_0_TX,
USB_AUDIO_TX_BACKEND,
+ BT_SCO_TX_BACKEND,
MAX_CODEC_BACKENDS
};
diff --git a/qahw_api/Android.mk b/qahw_api/Android.mk
index 8c99c5b..98e1a38 100644
--- a/qahw_api/Android.mk
+++ b/qahw_api/Android.mk
@@ -17,7 +17,8 @@
LOCAL_SHARED_LIBRARIES := \
liblog \
libcutils \
- libhardware
+ libhardware \
+ libdl
LOCAL_COPY_HEADERS_TO := mm-audio/qahw_api/inc
LOCAL_COPY_HEADERS := inc/qahw_api.h
diff --git a/qahw_api/inc/qahw_api.h b/qahw_api/inc/qahw_api.h
index 17f6f5f..1dc52f2 100644
--- a/qahw_api/inc/qahw_api.h
+++ b/qahw_api/inc/qahw_api.h
@@ -395,6 +395,12 @@
*/
int qahw_set_mode(qahw_module_handle_t *hw_module, audio_mode_t mode);
+/* Mute/unmute mic during voice/voip/HFP call */
+int qahw_set_mic_mute(qahw_module_handle_t *hw_module, bool state);
+
+/* Get mute/unmute status of mic during voice call */
+int qahw_get_mic_mute(qahw_module_handle_t *hw_module, bool *state);
+
/* set/get global audio parameters */
int qahw_set_parameters(qahw_module_handle_t *hw_module, const char *kv_pairs);
@@ -415,6 +421,13 @@
/*returns current QTI HAL version */
int qahw_get_version();
+/* Api to implement get parameters based on keyword param_id
+ * and store data in payload.
+ */
+int qahw_get_param_data(const qahw_module_handle_t *hw_module,
+ qahw_param_id param_id,
+ qahw_param_payload *payload);
+
__END_DECLS
#endif // QTI_AUDIO_HAL_API_H
diff --git a/qahw_api/inc/qahw_defs.h b/qahw_api/inc/qahw_defs.h
index 4441435..c3def18 100644
--- a/qahw_api/inc/qahw_defs.h
+++ b/qahw_api/inc/qahw_defs.h
@@ -162,6 +162,14 @@
#define QAHW_OFFLOAD_CODEC_VORBIS_BITSTREAM_FMT "music_offload_vorbis_bitstream_fmt"
+/* Set or Query stream profile type */
+#define QAHW_PARAMETER_STREAM_PROFILE "audio_stream_profile"
+
+/* audio inout flag for timestamp mode.
+ * check other input flags defined in audio.h for conflicts
+ */
+#define QAHW_INPUT_FLAG_TIMESTAMP 0x80000000
+
/* Query fm volume */
#define QAHW_PARAMETER_KEY_FM_VOLUME "fm_volume"
@@ -209,6 +217,32 @@
uint32_t reserved[64]; /*reserved for future */
} qahw_in_buffer_t;
+#define MAX_SECTORS 8
+
+struct qahw_source_tracking_param {
+ uint8_t vad[MAX_SECTORS];
+ uint16_t doa_speech;
+ uint16_t doa_noise[3];
+ uint8_t polar_activity[360];
+};
+
+struct qahw_sound_focus_param {
+ uint16_t start_angle[MAX_SECTORS];
+ uint8_t enable[MAX_SECTORS];
+ uint16_t gain_step;
+};
+
+typedef union {
+ struct qahw_source_tracking_param st_params;
+ struct qahw_sound_focus_param sf_params;
+} qahw_param_payload;
+
+typedef enum {
+ QAHW_PARAM_SOURCE_TRACK,
+ QAHW_PARAM_SOUND_FOCUS
+} qahw_param_id;
+
+
__END_DECLS
#endif // QTI_AUDIO_HAL_DEFS_H
diff --git a/qahw_api/src/qahw.c b/qahw_api/src/qahw.c
index 06bcee1..4025c56 100644
--- a/qahw_api/src/qahw.c
+++ b/qahw_api/src/qahw.c
@@ -31,6 +31,7 @@
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
+#include <dlfcn.h>
#include <utils/Log.h>
#include <stdlib.h>
#include <cutils/list.h>
@@ -46,6 +47,9 @@
*/
#define QAHW_MODULE_API_VERSION_CURRENT QAHW_MODULE_API_VERSION_0_0
+typedef uint64_t (*qahwi_in_read_v2_t)(audio_stream_in_t *in, void* buffer,
+ size_t bytes, uint64_t *timestamp);
+
typedef struct {
audio_hw_device_t *audio_device;
char module_name[MAX_MODULE_NAME_LENGTH];
@@ -54,6 +58,7 @@
struct listnode out_list;
pthread_mutex_t lock;
uint32_t ref_count;
+ const hw_module_t* module;
} qahw_module_t;
typedef struct {
@@ -74,6 +79,7 @@
qahw_module_t *module;
struct listnode list;
pthread_mutex_t lock;
+ qahwi_in_read_v2_t qahwi_in_read_v2;
} qahw_stream_in_t;
typedef enum {
@@ -85,6 +91,10 @@
static int qahw_list_count;
static pthread_mutex_t qahw_module_init_lock = PTHREAD_MUTEX_INITIALIZER;
+typedef int (*qahwi_get_param_data_t) (const struct audio_device *,
+ qahw_param_id, qahw_param_payload *);
+qahwi_get_param_data_t qahwi_get_param_data;
+
/** Start of internal functions */
/******************************************************************************/
@@ -916,8 +926,11 @@
pthread_mutex_lock(&qahw_stream_in->lock);
in = qahw_stream_in->stream;
- /*TBD:: call HAL timestamp read API*/
- if (in->read) {
+ if (qahw_stream_in->qahwi_in_read_v2) {
+ rc = qahw_stream_in->qahwi_in_read_v2(in, in_buf->buffer,
+ in_buf->bytes, in_buf->timestamp);
+ in_buf->offset = 0;
+ } else if (in->read) {
rc = in->read(in, in_buf->buffer, in_buf->bytes);
in_buf->offset = 0;
} else {
@@ -1078,6 +1091,64 @@
return rc;
}
+int qahw_set_mic_mute(qahw_module_handle_t *hw_module, bool state)
+{
+ int rc = -EINVAL;
+ qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
+ qahw_module_t *qahw_module_temp;
+
+ pthread_mutex_lock(&qahw_module_init_lock);
+ qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
+ pthread_mutex_unlock(&qahw_module_init_lock);
+ if (qahw_module_temp == NULL) {
+ ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
+ goto exit;
+ }
+
+ pthread_mutex_lock(&qahw_module->lock);
+ if (qahw_module->audio_device->set_mic_mute) {
+ rc = qahw_module->audio_device->set_mic_mute(qahw_module->audio_device,
+ state);
+ } else {
+ rc = -ENOSYS;
+ ALOGW("%s not supported", __func__);
+ }
+ pthread_mutex_unlock(&qahw_module->lock);
+
+exit:
+ return rc;
+}
+
+int qahw_get_mic_mute(qahw_module_handle_t *hw_module, bool *state)
+{
+ size_t rc = 0;
+ qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
+ qahw_module_t *qahw_module_temp;
+ audio_hw_device_t *audio_device;
+
+ pthread_mutex_lock(&qahw_module_init_lock);
+ qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
+ pthread_mutex_unlock(&qahw_module_init_lock);
+ if (qahw_module_temp == NULL) {
+ ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
+ goto exit;
+ }
+
+ pthread_mutex_lock(&qahw_module->lock);
+ audio_device = qahw_module->audio_device;
+ if (qahw_module->audio_device->get_mic_mute) {
+ rc = audio_device->get_mic_mute(qahw_module->audio_device,
+ &state);
+ } else {
+ rc = -ENOSYS;
+ ALOGW("%s not supported", __func__);
+ }
+ pthread_mutex_unlock(&qahw_module->lock);
+
+exit:
+ return rc;
+}
+
/* set/get global audio parameters */
int qahw_set_parameters(qahw_module_handle_t *hw_module, const char *kv_pairs)
{
@@ -1141,6 +1212,39 @@
return str_param;
}
+/* Api to implement get parameters based on keyword param_id
+ * and store data in payload.
+ */
+int qahw_get_param_data(const qahw_module_handle_t *hw_module,
+ qahw_param_id param_id,
+ qahw_param_payload *payload)
+{
+ int ret = 0;
+ qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
+ qahw_module_t *qahw_module_temp;
+
+ pthread_mutex_lock(&qahw_module_init_lock);
+ qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
+ pthread_mutex_unlock(&qahw_module_init_lock);
+ if (qahw_module_temp == NULL) {
+ ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
+ goto exit;
+ }
+
+ pthread_mutex_lock(&qahw_module->lock);
+
+ if (qahwi_get_param_data){
+ ret = qahwi_get_param_data (qahw_module->audio_device, param_id, payload);
+ } else {
+ ret = -ENOSYS;
+ ALOGE("%s not supported\n",__func__);
+ }
+ pthread_mutex_unlock(&qahw_module->lock);
+
+exit:
+ return ret;
+}
+
/* Returns audio input buffer size according to parameters passed or
* 0 if one of the parameters is not supported.
* See also get_buffer_size which is for a particular stream.
@@ -1322,6 +1426,20 @@
list_add_tail(&qahw_module->in_list, &qahw_stream_in->list);
}
+ /* dlsym qahwi_in_read_v2 if timestamp flag is used */
+ if (!rc && (flags & QAHW_INPUT_FLAG_TIMESTAMP)) {
+ const char *error;
+
+ /* clear any existing errors */
+ dlerror();
+ qahw_stream_in->qahwi_in_read_v2 = (qahwi_in_read_v2_t)
+ dlsym(qahw_module->module->dso, "qahwi_in_read_v2");
+ if ((error = dlerror()) != NULL) {
+ ALOGI("%s: dlsym error %s for qahwi_in_read_v2", __func__, error);
+ qahw_stream_in->qahwi_in_read_v2 = NULL;
+ }
+ }
+
exit:
pthread_mutex_unlock(&qahw_module->lock);
return rc;
@@ -1423,8 +1541,14 @@
audio_hw_device_close(audio_device);
goto error_exit;
}
+ qahw_module->module = module;
ALOGD("%s::Loaded HAL %s module %p", __func__, ahal_name, qahw_module);
+ qahwi_get_param_data = (qahwi_get_param_data_t) dlsym (module->dso,
+ "qahwi_get_param_data");
+ if (!qahwi_get_param_data)
+ ALOGD("%s::qahwi_get_param_data api is not defined\n",__func__);
+
if (!qahw_list_count)
list_init(&qahw_module_list);
qahw_list_count++;