Merge "Fix security vulnerability: Equalizer setParameter memory overflow" into lmp-dev am: 1f0f83e1ff am: b3ad2a046e am: dfd990face am: 9f623d6475 am: d4517e643b am: f4c3975abf am: 878d778986 am: a31de317d4 am: 025df90241 am: 853a6bc15f am: d9a108d531
am: 3f7ffd5d56
Change-Id: Icd744fb5e1f076a663887bf07c2507e2a1d355e3
diff --git a/hal/Android.mk b/hal/Android.mk
index f3d2798..2b27e64 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -74,7 +74,13 @@
LOCAL_SRC_FILES += $(AUDIO_PLATFORM)/hw_info.c
endif
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_USB_TUNNEL)),true)
+ LOCAL_CFLAGS += -DUSB_TUNNEL_ENABLED
+ LOCAL_SRC_FILES += audio_extn/usb.c
+endif
+
LOCAL_SHARED_LIBRARIES := \
+ libaudioutils \
liblog \
libcutils \
libtinyalsa \
@@ -122,6 +128,10 @@
LOCAL_CFLAGS += -DSOUND_TRIGGER_PLATFORM_NAME=$(TARGET_BOARD_PLATFORM)
LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/sound_trigger
LOCAL_SRC_FILES += audio_extn/soundtrigger.c
+ifneq ($(filter msm8996,$(TARGET_BOARD_PLATFORM)),)
+LOCAL_HEADER_LIBRARIES := sound_trigger.primary_headers
+endif
+
endif
ifeq ($(strip $(AUDIO_FEATURE_ENABLED_SPKR_PROTECTION)),true)
@@ -152,6 +162,12 @@
LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_OWNER := qcom
+
+LOCAL_PROPRIETARY_MODULE := true
+
+LOCAL_CFLAGS += -Werror
+
include $(BUILD_SHARED_LIBRARY)
endif
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index f53c9f2..b734cb6 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -70,6 +70,31 @@
#endif
+#ifndef USB_TUNNEL_ENABLED
+#define audio_extn_usb_init(adev) (0)
+#define audio_extn_usb_deinit() (0)
+#define audio_extn_usb_add_device(device, card) (0)
+#define audio_extn_usb_remove_device(device, card) (0)
+#define audio_extn_usb_is_config_supported(bit_width, sample_rate, ch, pb) (false)
+#define audio_extn_usb_enable_sidetone(device, enable) (0)
+#define audio_extn_usb_set_sidetone_gain(parms, value, len) (0)
+#define audio_extn_usb_is_capture_supported() (false)
+#else
+void audio_extn_usb_init(void *adev);
+void audio_extn_usb_deinit();
+void audio_extn_usb_add_device(audio_devices_t device, int card);
+void audio_extn_usb_remove_device(audio_devices_t device, int card);
+bool audio_extn_usb_is_config_supported(unsigned int *bit_width,
+ unsigned int *sample_rate,
+ unsigned int *ch,
+ bool is_playback);
+int audio_extn_usb_enable_sidetone(int device, bool enable);
+int audio_extn_usb_set_sidetone_gain(struct str_parms *parms,
+ char *value, int len);
+bool audio_extn_usb_is_capture_supported();
+#endif
+
+
#ifndef SOUND_TRIGGER_ENABLED
#define audio_extn_sound_trigger_init(adev) (0)
#define audio_extn_sound_trigger_deinit(adev) (0)
diff --git a/hal/audio_extn/ext_speaker.c b/hal/audio_extn/ext_speaker.c
index 080620d..1bf8e5b 100644
--- a/hal/audio_extn/ext_speaker.c
+++ b/hal/audio_extn/ext_speaker.c
@@ -23,9 +23,9 @@
#include <dlfcn.h>
#ifdef __LP64__
-#define LIB_SPEAKER_BUNDLE "/system/lib64/soundfx/libspeakerbundle.so"
+#define LIB_SPEAKER_BUNDLE "/vendor/lib64/soundfx/libspeakerbundle.so"
#else
-#define LIB_SPEAKER_BUNDLE "/system/lib/soundfx/libspeakerbundle.so"
+#define LIB_SPEAKER_BUNDLE "/vendor/lib/soundfx/libspeakerbundle.so"
#endif
typedef void (*set_mode_t)(int);
diff --git a/hal/audio_extn/sndmonitor.c b/hal/audio_extn/sndmonitor.c
index d303c06..58ab43d 100644
--- a/hal/audio_extn/sndmonitor.c
+++ b/hal/audio_extn/sndmonitor.c
@@ -190,8 +190,10 @@
}
ret = add_new_sndcard(atoi(ptr), fd);
- if (ret != 0)
+ if (ret != 0) {
+ close(fd); // card state fd ownership is taken by sndcard on success
continue;
+ }
num_cards++;
@@ -218,8 +220,10 @@
continue;
ret = add_new_sndcard(CPE_MAGIC_NUM+num_cpe, fd);
- if (ret != 0)
+ if (ret != 0) {
+ close(fd); // card state fd ownership is taken by sndcard on success
continue;
+ }
num_cpe++;
num_cards++;
@@ -578,8 +582,12 @@
write(sndmonitor.intpipe[1], "Q", 1);
pthread_join(sndmonitor.monitor_thread, (void **) NULL);
- free_sndcards();
free_dev_events();
+ listeners_deinit();
+ free_sndcards();
+ close(sndmonitor.intpipe[0]);
+ close(sndmonitor.intpipe[1]);
+
sndmonitor.initcheck = 0;
return 0;
}
@@ -593,13 +601,13 @@
sndmonitor.initcheck = false;
if (pipe(sndmonitor.intpipe) < 0)
- return -ENODEV;
+ goto pipe_error;
if (enum_sndcards() < 0)
- return -ENODEV;
+ goto enum_sncards_error;
if (listeners_init() < 0)
- return -ENODEV;
+ goto listeners_error;
#ifdef MONITOR_DEVICE_EVENTS
enum_dev_events(); // failure here isn't fatal
@@ -610,14 +618,20 @@
monitor_thread_loop, NULL);
if (ret) {
- free_sndcards();
- free_dev_events();
- close(sndmonitor.intpipe[0]);
- close(sndmonitor.intpipe[1]);
- return -ENODEV;
+ goto monitor_thread_create_error;
}
sndmonitor.initcheck = true;
return 0;
+
+monitor_thread_create_error:
+ listeners_deinit();
+listeners_error:
+ free_sndcards();
+enum_sncards_error:
+ close(sndmonitor.intpipe[0]);
+ close(sndmonitor.intpipe[1]);
+pipe_error:
+ return -ENODEV;
}
int audio_extn_snd_mon_register_listener(void *stream, snd_mon_cb cb)
diff --git a/hal/audio_extn/usb.c b/hal/audio_extn/usb.c
new file mode 100644
index 0000000..3747318
--- /dev/null
+++ b/hal/audio_extn/usb.c
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_usb"
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <system/audio.h>
+#include <tinyalsa/asoundlib.h>
+#include <audio_hw.h>
+#include <cutils/properties.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifdef USB_TUNNEL_ENABLED
+#define USB_BUFF_SIZE 2048
+#define CHANNEL_NUMBER_STR "Channels: "
+#define PLAYBACK_PROFILE_STR "Playback:"
+#define CAPTURE_PROFILE_STR "Capture:"
+#define USB_SIDETONE_GAIN_STR "usb_sidetone_gain"
+#define ABS_SUB(A, B) (((A) > (B)) ? ((A) - (B)):((B) - (A)))
+#define SAMPLE_RATE_8000 8000
+#define SAMPLE_RATE_11025 11025
+/* TODO: dynamically populate supported sample rates */
+static uint32_t supported_sample_rates[] =
+ {44100, 48000, 64000, 88200, 96000, 176400, 192000, 384000};
+
+#define MAX_SAMPLE_RATE_SIZE sizeof(supported_sample_rates)/sizeof(supported_sample_rates[0])
+
+enum usb_usecase_type{
+ USB_PLAYBACK = 0,
+ USB_CAPTURE,
+};
+
+enum {
+ USB_SIDETONE_ENABLE_INDEX = 0,
+ USB_SIDETONE_VOLUME_INDEX,
+ USB_SIDETONE_MAX_INDEX,
+};
+
+struct usb_device_config {
+ struct listnode list;
+ unsigned int bit_width;
+ unsigned int channel_count;
+ unsigned int rate_size;
+ unsigned int rates[MAX_SAMPLE_RATE_SIZE];
+};
+
+struct usb_card_config {
+ struct listnode list;
+ audio_devices_t usb_device_type;
+ int usb_card;
+ struct listnode usb_device_conf_list;
+ struct mixer *usb_snd_mixer;
+ int usb_sidetone_index[USB_SIDETONE_MAX_INDEX];
+ int usb_sidetone_vol_min;
+ int usb_sidetone_vol_max;
+};
+
+struct usb_module {
+ struct listnode usb_card_conf_list;
+ struct audio_device *adev;
+ int sidetone_gain;
+ bool is_capture_supported;
+};
+
+static struct usb_module *usbmod = NULL;
+static bool usb_audio_debug_enable = false;
+static int usb_sidetone_gain = 0;
+
+static const char * const usb_sidetone_enable_str[] = {
+ "Sidetone Playback Switch",
+ "Mic Playback Switch",
+};
+
+static const char * const usb_sidetone_volume_str[] = {
+ "Sidetone Playback Volume",
+ "Mic Playback Volume",
+};
+
+static void usb_mixer_print_enum(struct mixer_ctl *ctl)
+{
+ unsigned int num_enums;
+ unsigned int i;
+ const char *string;
+
+ num_enums = mixer_ctl_get_num_enums(ctl);
+
+ for (i = 0; i < num_enums; i++) {
+ string = mixer_ctl_get_enum_string(ctl, i);
+ ALOGI("\t%s%s", mixer_ctl_get_value(ctl, 0) == (int)i ? ">" : "", string);
+ }
+}
+
+static void usb_soundcard_detail_control(struct mixer *mixer, const char *control)
+{
+ struct mixer_ctl *ctl;
+ enum mixer_ctl_type type;
+ unsigned int num_values;
+ unsigned int i;
+ int min, max;
+
+ if (isdigit(control[0]))
+ ctl = mixer_get_ctl(mixer, atoi(control));
+ else
+ ctl = mixer_get_ctl_by_name(mixer, control);
+
+ if (!ctl) {
+ fprintf(stderr, "Invalid mixer control\n");
+ return;
+ }
+
+ type = mixer_ctl_get_type(ctl);
+ num_values = mixer_ctl_get_num_values(ctl);
+
+ ALOGV("%s:", mixer_ctl_get_name(ctl));
+
+ for (i = 0; i < num_values; i++) {
+ switch (type) {
+ case MIXER_CTL_TYPE_INT:
+ ALOGV(" %d", mixer_ctl_get_value(ctl, i));
+ break;
+ case MIXER_CTL_TYPE_BOOL:
+ ALOGV(" %s", mixer_ctl_get_value(ctl, i) ? "On" : "Off");
+ break;
+ case MIXER_CTL_TYPE_ENUM:
+ usb_mixer_print_enum(ctl);
+ break;
+ case MIXER_CTL_TYPE_BYTE:
+ ALOGV(" 0x%02x", mixer_ctl_get_value(ctl, i));
+ break;
+ default:
+ ALOGV(" unknown");
+ break;
+ }
+ }
+
+ if (type == MIXER_CTL_TYPE_INT) {
+ min = mixer_ctl_get_range_min(ctl);
+ max = mixer_ctl_get_range_max(ctl);
+ ALOGV(" (range %d->%d)", min, max);
+ }
+}
+
+static void usb_soundcard_list_controls(struct mixer *mixer)
+{
+ struct mixer_ctl *ctl;
+ const char *name, *type;
+ unsigned int num_ctls, num_values;
+ unsigned int i;
+
+ num_ctls = mixer_get_num_ctls(mixer);
+
+ ALOGV("Number of controls: %d\n", num_ctls);
+
+ ALOGV("ctl\ttype\tnum\t%-40s value\n", "name");
+ for (i = 0; i < num_ctls; i++) {
+ ctl = mixer_get_ctl(mixer, i);
+ if (ctl != NULL) {
+ name = mixer_ctl_get_name(ctl);
+ type = mixer_ctl_get_type_string(ctl);
+ num_values = mixer_ctl_get_num_values(ctl);
+ ALOGV("%d\t%s\t%d\t%-40s", i, type, num_values, name);
+ if (name != NULL)
+ usb_soundcard_detail_control(mixer, name);
+ }
+ }
+}
+
+static int usb_set_dev_id_mixer_ctl(unsigned int usb_usecase_type, int card,
+ char *dev_mixer_ctl_name)
+{
+ struct mixer_ctl *ctl;
+ unsigned int dev_token;
+ const unsigned int pcm_device_number = 0;
+
+ /*
+ * usb_dev_token_id is 32 bit number and is defined as below:
+ * usb_sound_card_idx(31:16) | usb PCM device ID(15:8) | usb_usecase_type(7:0)
+ */
+ dev_token = (card << 16 ) |
+ (pcm_device_number << 8) | (usb_usecase_type & 0xFF);
+
+ ctl = mixer_get_ctl_by_name(usbmod->adev->mixer, dev_mixer_ctl_name);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",
+ __func__, dev_mixer_ctl_name);
+ return -EINVAL;
+ }
+ mixer_ctl_set_value(ctl, 0, dev_token);
+
+ return 0;
+}
+
+static int usb_get_sample_rates(char *rates_str,
+ struct usb_device_config *config)
+{
+ uint32_t i;
+ char *next_sr_string, *temp_ptr;
+ uint32_t sr, min_sr, max_sr, sr_size = 0;
+
+ /* Sample rate string can be in any of the folloing two bit_widthes:
+ * Rates: 8000 - 48000 (continuous)
+ * Rates: 8000, 44100, 48000
+ * Support both the bit_widths
+ */
+ ALOGV("%s: rates_str %s", __func__, rates_str);
+ next_sr_string = strtok_r(rates_str, "Rates: ", &temp_ptr);
+ if (next_sr_string == NULL) {
+ ALOGE("%s: could not find min rates string", __func__);
+ return -EINVAL;
+ }
+ if (strstr(rates_str, "continuous") != NULL) {
+ min_sr = (uint32_t)atoi(next_sr_string);
+ next_sr_string = strtok_r(NULL, " ,.-", &temp_ptr);
+ if (next_sr_string == NULL) {
+ ALOGE("%s: could not find max rates string", __func__);
+ return -EINVAL;
+ }
+ max_sr = (uint32_t)atoi(next_sr_string);
+
+ for (i = 0; i < MAX_SAMPLE_RATE_SIZE; i++) {
+ if (supported_sample_rates[i] >= min_sr &&
+ supported_sample_rates[i] <= max_sr) {
+ config->rates[sr_size++] = supported_sample_rates[i];
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: continuous sample rate supported_sample_rates[%d] %d",
+ __func__, i, supported_sample_rates[i]);
+ }
+ }
+ } else {
+ do {
+ sr = (uint32_t)atoi(next_sr_string);
+ for (i = 0; i < MAX_SAMPLE_RATE_SIZE; i++) {
+ if (supported_sample_rates[i] == sr) {
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: sr %d, supported_sample_rates[%d] %d -> matches!!",
+ __func__, sr, i, supported_sample_rates[i]);
+ config->rates[sr_size++] = supported_sample_rates[i];
+ }
+ }
+ next_sr_string = strtok_r(NULL, " ,.-", &temp_ptr);
+ } while (next_sr_string != NULL);
+ }
+ config->rate_size = sr_size;
+ return 0;
+}
+
+static int usb_get_capability(int type,
+ struct usb_card_config *usb_card_info,
+ int card)
+{
+ int32_t size = 0;
+ int32_t fd=-1;
+ int32_t channels_no;
+ char *str_start = NULL;
+ char *str_end = NULL;
+ char *channel_start = NULL;
+ char *bit_width_start = NULL;
+ char *rates_str_start = NULL;
+ char *target = NULL;
+ char *read_buf = NULL;
+ char *rates_str = NULL;
+ char path[128];
+ int ret = 0;
+ char *bit_width_str = NULL;
+ struct usb_device_config * usb_device_info;
+ bool check = false;
+
+ memset(path, 0, sizeof(path));
+ ALOGV("%s: for %s", __func__, (type == USB_PLAYBACK) ?
+ PLAYBACK_PROFILE_STR : CAPTURE_PROFILE_STR);
+
+ /* TODO: convert the below to using alsa_utils */
+ ret = snprintf(path, sizeof(path), "/proc/asound/card%u/stream0",
+ card);
+ if(ret < 0) {
+ ALOGE("%s: failed on snprintf (%d) to path %s\n",
+ __func__, ret, path);
+ goto done;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd <0) {
+ ALOGE("%s: error failed to open config file %s error: %d\n",
+ __func__, path, errno);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ read_buf = (char *)calloc(1, USB_BUFF_SIZE + 1);
+
+ if (!read_buf) {
+ ALOGE("Failed to create read_buf");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if(read(fd, read_buf, USB_BUFF_SIZE) < 0) {
+ ALOGE("file read error\n");
+ goto done;
+ }
+ str_start = strstr(read_buf, ((type == USB_PLAYBACK) ?
+ PLAYBACK_PROFILE_STR : CAPTURE_PROFILE_STR));
+ if (str_start == NULL) {
+ ALOGE("%s: error %s section not found in usb config file",
+ __func__, ((type == USB_PLAYBACK) ?
+ PLAYBACK_PROFILE_STR : CAPTURE_PROFILE_STR));
+ ret = -EINVAL;
+ goto done;
+ }
+ str_end = strstr(read_buf, ((type == USB_PLAYBACK) ?
+ CAPTURE_PROFILE_STR : PLAYBACK_PROFILE_STR));
+ if (str_end > str_start)
+ check = true;
+
+ ALOGV("%s: usb_config = %s, check %d\n", __func__, str_start, check);
+
+ while (str_start != NULL) {
+ str_start = strstr(str_start, "Altset");
+ if ((str_start == NULL) || (check && (str_start >= str_end))) {
+ ALOGV("%s: done parsing %s\n", __func__, str_start);
+ break;
+ }
+ ALOGV("%s: remaining string %s\n", __func__, str_start);
+ str_start += sizeof("Altset");
+ usb_device_info = calloc(1, sizeof(struct usb_device_config));
+ if (usb_device_info == NULL) {
+ ALOGE("%s: error unable to allocate memory",
+ __func__);
+ ret = -ENOMEM;
+ break;
+ }
+ /* Bit bit_width parsing */
+ bit_width_start = strstr(str_start, "Format: ");
+ if (bit_width_start == NULL) {
+ ALOGI("%s: Could not find bit_width string", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ target = strchr(bit_width_start, '\n');
+ if (target == NULL) {
+ ALOGI("%s:end of line not found", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ size = target - bit_width_start;
+ if ((bit_width_str = (char *)malloc(size + 1)) == NULL) {
+ ALOGE("%s: unable to allocate memory to hold bit width strings",
+ __func__);
+ ret = -EINVAL;
+ free(usb_device_info);
+ break;
+ }
+ memcpy(bit_width_str, bit_width_start, size);
+ bit_width_str[size] = '\0';
+ if (strstr(bit_width_str, "S16_LE"))
+ usb_device_info->bit_width = 16;
+ else if (strstr(bit_width_str, "S24_LE"))
+ usb_device_info->bit_width = 24;
+ else if (strstr(bit_width_str, "S24_3LE"))
+ usb_device_info->bit_width = 24;
+ else if (strstr(bit_width_str, "S32_LE"))
+ usb_device_info->bit_width = 32;
+
+ if (bit_width_str)
+ free(bit_width_str);
+
+ /* channels parsing */
+ channel_start = strstr(str_start, CHANNEL_NUMBER_STR);
+ if (channel_start == NULL) {
+ ALOGI("%s: could not find Channels string", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ channels_no = atoi(channel_start + strlen(CHANNEL_NUMBER_STR));
+ usb_device_info->channel_count = channels_no;
+
+ /* Sample rates parsing */
+ rates_str_start = strstr(str_start, "Rates: ");
+ if (rates_str_start == NULL) {
+ ALOGI("%s: cant find rates string", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ target = strchr(rates_str_start, '\n');
+ if (target == NULL) {
+ ALOGI("%s: end of line not found", __func__);
+ free(usb_device_info);
+ continue;
+ }
+ size = target - rates_str_start;
+ if ((rates_str = (char *)malloc(size + 1)) == NULL) {
+ ALOGE("%s: unable to allocate memory to hold sample rate strings",
+ __func__);
+ ret = -EINVAL;
+ free(usb_device_info);
+ break;
+ }
+ memcpy(rates_str, rates_str_start, size);
+ rates_str[size] = '\0';
+ ret = usb_get_sample_rates(rates_str, usb_device_info);
+ if (rates_str)
+ free(rates_str);
+ if (ret < 0) {
+ ALOGE("%s: error unable to get sample rate values",
+ __func__);
+ free(usb_device_info);
+ continue;
+ }
+ /* Add to list if every field is valid */
+ list_add_tail(&usb_card_info->usb_device_conf_list,
+ &usb_device_info->list);
+ }
+
+done:
+ if (fd >= 0) close(fd);
+ if (read_buf) free(read_buf);
+ return ret;
+}
+
+static int usb_get_device_playback_config(struct usb_card_config *usb_card_info,
+ int card)
+{
+ int ret;
+
+ /* get capabilities */
+ if ((ret = usb_get_capability(USB_PLAYBACK, usb_card_info, card))) {
+ ALOGE("%s: could not get Playback capabilities from usb device",
+ __func__);
+ goto exit;
+ }
+ usb_set_dev_id_mixer_ctl(USB_PLAYBACK, card, "USB_AUDIO_RX dev_token");
+
+exit:
+
+ return ret;
+}
+
+static int usb_get_device_capture_config(struct usb_card_config *usb_card_info,
+ int card)
+{
+ int ret;
+
+ /* get capabilities */
+ if ((ret = usb_get_capability(USB_CAPTURE, usb_card_info, card))) {
+ ALOGE("%s: could not get Playback capabilities from usb device",
+ __func__);
+ goto exit;
+ }
+ usb_set_dev_id_mixer_ctl(USB_CAPTURE, card, "USB_AUDIO_TX dev_token");
+
+exit:
+ return ret;
+}
+
+static void usb_get_sidetone_mixer(struct usb_card_config *usb_card_info)
+{
+ struct mixer_ctl *ctl;
+ unsigned int index;
+
+ for (index = 0; index < USB_SIDETONE_MAX_INDEX; index++)
+ usb_card_info->usb_sidetone_index[index] = -1;
+
+ usb_card_info->usb_snd_mixer = mixer_open(usb_card_info->usb_card);
+ for (index = 0;
+ index < sizeof(usb_sidetone_enable_str)/sizeof(usb_sidetone_enable_str[0]);
+ index++) {
+ ctl = mixer_get_ctl_by_name(usb_card_info->usb_snd_mixer,
+ usb_sidetone_enable_str[index]);
+ if (ctl) {
+ usb_card_info->usb_sidetone_index[USB_SIDETONE_ENABLE_INDEX] = index;
+ /* Disable device sidetone by default */
+ mixer_ctl_set_value(ctl, 0, false);
+ break;
+ }
+ }
+ for (index = 0;
+ index < sizeof(usb_sidetone_volume_str)/sizeof(usb_sidetone_volume_str[0]);
+ index++) {
+ ctl = mixer_get_ctl_by_name(usb_card_info->usb_snd_mixer,
+ usb_sidetone_volume_str[index]);
+ if (ctl) {
+ usb_card_info->usb_sidetone_index[USB_SIDETONE_VOLUME_INDEX] = index;
+ usb_card_info->usb_sidetone_vol_min = mixer_ctl_get_range_min(ctl);
+ usb_card_info->usb_sidetone_vol_max = mixer_ctl_get_range_max(ctl);
+ break;
+ }
+ }
+
+ if ((usb_card_info->usb_snd_mixer != NULL) && (usb_audio_debug_enable))
+ usb_soundcard_list_controls(usb_card_info->usb_snd_mixer);
+
+ return;
+}
+
+static bool usb_valid_device(audio_devices_t device)
+{
+ if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_USB_DEVICE))
+ return true;
+
+ if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
+ device &= ~AUDIO_DEVICE_BIT_IN;
+ if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_USB_DEVICE) != 0)
+ return true;
+ }
+
+ return false;
+}
+
+static void usb_print_active_device(void){
+ struct listnode *node_i, *node_j;
+ struct usb_device_config *dev_info;
+ struct usb_card_config *card_info;
+ unsigned int i;
+
+ ALOGI("%s", __func__);
+ list_for_each(node_i, &usbmod->usb_card_conf_list) {
+ card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGI("%s: card_dev_type (0x%x), card_no(%d)",
+ __func__, card_info->usb_device_type, card_info->usb_card);
+ list_for_each(node_j, &card_info->usb_device_conf_list) {
+ dev_info = node_to_item(node_j, struct usb_device_config, list);
+ ALOGI("%s: bit-width(%d) channel(%d)",
+ __func__, dev_info->bit_width, dev_info->channel_count);
+ for (i = 0; i < dev_info->rate_size; i++)
+ ALOGI("%s: rate %d", __func__, dev_info->rates[i]);
+ }
+ }
+}
+
+static bool usb_get_best_match_for_bit_width(
+ struct listnode *dev_list,
+ unsigned int stream_bit_width,
+ unsigned int *bit_width)
+{
+ struct listnode *node_i;
+ struct usb_device_config *dev_info;
+ unsigned int candidate = 0;
+
+ list_for_each(node_i, dev_list) {
+ dev_info = node_to_item(node_i, struct usb_device_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: USB bw(%d), stream bw(%d), candidate(%d)",
+ __func__, dev_info->bit_width,
+ stream_bit_width, candidate);
+ if (dev_info->bit_width == stream_bit_width) {
+ *bit_width = dev_info->bit_width;
+ ALOGV("%s: Found match bit-width (%d)",
+ __func__, dev_info->bit_width);
+ goto exit;
+ } else if (candidate == 0) {
+ candidate = dev_info->bit_width;
+ }
+ /*
+ * If stream bit is 24, USB supports both 16 bit and 32 bit, then
+ * higher bit width 32 is picked up instead of 16-bit
+ */
+ else if (ABS_SUB(stream_bit_width, dev_info->bit_width) <
+ ABS_SUB(stream_bit_width, candidate)) {
+ candidate = dev_info->bit_width;
+ }
+ else if ((ABS_SUB(stream_bit_width, dev_info->bit_width) ==
+ ABS_SUB(stream_bit_width, candidate)) &&
+ (dev_info->bit_width > candidate)) {
+ candidate = dev_info->bit_width;
+ }
+ }
+ ALOGV("%s: No match found, use the best candidate bw(%d)",
+ __func__, candidate);
+ *bit_width = candidate;
+exit:
+ return true;
+}
+
+static bool usb_get_best_match_for_channels(
+ struct listnode *dev_list,
+ unsigned int bit_width,
+ unsigned int stream_ch,
+ unsigned int *channel_count)
+{
+ struct listnode *node_i;
+ struct usb_device_config *dev_info;
+ unsigned int candidate = 0;
+
+ list_for_each(node_i, dev_list) {
+ dev_info = node_to_item(node_i, struct usb_device_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: USB ch(%d)bw(%d), stream ch(%d)bw(%d), candidate(%d)",
+ __func__, dev_info->channel_count, dev_info->bit_width,
+ stream_ch, bit_width, candidate);
+ if (dev_info->bit_width != bit_width)
+ continue;
+ if (dev_info->channel_count== stream_ch) {
+ *channel_count = dev_info->channel_count;
+ ALOGV("%s: Found match channels (%d)",
+ __func__, dev_info->channel_count);
+ goto exit;
+ } else if (candidate == 0)
+ candidate = dev_info->channel_count;
+ /*
+ * If stream channel is 4, USB supports both 3 and 5, then
+ * higher channel 5 is picked up instead of 3
+ */
+ else if (ABS_SUB(stream_ch, dev_info->channel_count) <
+ ABS_SUB(stream_ch, candidate)) {
+ candidate = dev_info->channel_count;
+ } else if ((ABS_SUB(stream_ch, dev_info->channel_count) ==
+ ABS_SUB(stream_ch, candidate)) &&
+ (dev_info->channel_count > candidate)) {
+ candidate = dev_info->channel_count;
+ }
+ }
+ ALOGV("%s: No match found, use the best candidate ch(%d)",
+ __func__, candidate);
+ *channel_count = candidate;
+exit:
+ return true;
+
+}
+
+static bool usb_sample_rate_multiple(
+ unsigned int stream_sample_rate,
+ unsigned int base)
+{
+ return (((stream_sample_rate / base) * base) == stream_sample_rate);
+}
+
+static bool usb_find_sample_rate_candidate(unsigned int base,
+ unsigned stream_rate,
+ unsigned int usb_rate,
+ unsigned int cur_candidate,
+ unsigned int *update_candidate)
+{
+ /* For sample rate, we should consider fracational sample rate as high priority.
+ * For example, if the stream is 88.2kHz and USB device support both 44.1kH and
+ * 48kHz sample rate, we should pick 44.1kHz instead of 48kHz
+ */
+ if (!usb_sample_rate_multiple(cur_candidate, base) &&
+ usb_sample_rate_multiple(usb_rate, base)) {
+ *update_candidate = usb_rate;
+ } else if (usb_sample_rate_multiple(cur_candidate, base) &&
+ usb_sample_rate_multiple(usb_rate, base)) {
+ if (ABS_SUB(stream_rate, usb_rate) <
+ ABS_SUB(stream_rate, cur_candidate)) {
+ *update_candidate = usb_rate;
+ } else if ((ABS_SUB(stream_rate, usb_rate) ==
+ ABS_SUB(stream_rate, cur_candidate)) &&
+ (usb_rate > cur_candidate)) {
+ *update_candidate = usb_rate;
+ }
+ } else if (!usb_sample_rate_multiple(cur_candidate, base) &&
+ !usb_sample_rate_multiple(usb_rate, base)) {
+ if (ABS_SUB(stream_rate, usb_rate) <
+ ABS_SUB(stream_rate, cur_candidate)) {
+ *update_candidate = usb_rate;
+ } else if ((ABS_SUB(stream_rate, usb_rate) ==
+ ABS_SUB(stream_rate, cur_candidate)) &&
+ (usb_rate > cur_candidate)) {
+ *update_candidate = usb_rate;
+ }
+ }
+ return true;
+}
+
+static bool usb_get_best_match_for_sample_rate(
+ struct listnode *dev_list,
+ unsigned int bit_width,
+ unsigned int channel_count,
+ unsigned int stream_sample_rate,
+ unsigned int *sr)
+{
+ struct listnode *node_i;
+ struct usb_device_config *dev_info;
+ unsigned int candidate = 48000;
+ unsigned int base = SAMPLE_RATE_8000;
+ bool multiple_8k = usb_sample_rate_multiple(stream_sample_rate, base);
+ unsigned int i;
+
+ ALOGV("%s: stm ch(%d)bw(%d)sr(%d), stream sample multiple of 8kHz(%d)",
+ __func__, channel_count, bit_width, stream_sample_rate, multiple_8k);
+
+ list_for_each(node_i, dev_list) {
+ dev_info = node_to_item(node_i, struct usb_device_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: USB ch(%d)bw(%d), stm ch(%d)bw(%d)sr(%d), candidate(%d)",
+ __func__, dev_info->channel_count, dev_info->bit_width,
+ channel_count, bit_width, stream_sample_rate, candidate);
+ if ((dev_info->bit_width != bit_width) || dev_info->channel_count != channel_count)
+ continue;
+
+ candidate = 0;
+ for (i = 0; i < dev_info->rate_size; i++) {
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: USB ch(%d)bw(%d)sr(%d), stm ch(%d)bw(%d)sr(%d), candidate(%d)",
+ __func__, dev_info->channel_count,
+ dev_info->bit_width, dev_info->rates[i],
+ channel_count, bit_width, stream_sample_rate, candidate);
+ if (stream_sample_rate == dev_info->rates[i]) {
+ *sr = dev_info->rates[i];
+ ALOGV("%s: Found match sample rate (%d)",
+ __func__, dev_info->rates[i]);
+ goto exit;
+ } else if (candidate == 0) {
+ candidate = dev_info->rates[i];
+ /*
+ * For sample rate, we should consider fracational sample rate as high priority.
+ * For example, if the stream is 88.2kHz and USB device support both 44.1kH and
+ * 48kHz sample rate, we should pick 44.1kHz instead of 48kHz
+ */
+ } else if (multiple_8k) {
+ usb_find_sample_rate_candidate(SAMPLE_RATE_8000,
+ stream_sample_rate,
+ dev_info->rates[i],
+ candidate,
+ &candidate);
+ } else {
+ usb_find_sample_rate_candidate(SAMPLE_RATE_11025,
+ stream_sample_rate,
+ dev_info->rates[i],
+ candidate,
+ &candidate);
+ }
+ }
+ }
+ ALOGV("%s: No match found, use the best candidate sr(%d)",
+ __func__, candidate);
+ *sr = candidate;
+exit:
+ return true;
+}
+
+static bool usb_audio_backend_apply_policy(struct listnode *dev_list,
+ unsigned int *bit_width,
+ unsigned int *sample_rate,
+ unsigned int *channel_count)
+{
+ ALOGV("%s: from stream: bit-width(%d) sample_rate(%d) channels (%d)",
+ __func__, *bit_width, *sample_rate, *channel_count);
+ if (list_empty(dev_list)) {
+ *sample_rate = 48000;
+ *bit_width = 16;
+ *channel_count = 2;
+ ALOGE("%s: list is empty,fall back to default setting", __func__);
+ goto exit;
+ }
+ usb_get_best_match_for_bit_width(dev_list, *bit_width, bit_width);
+ usb_get_best_match_for_channels(dev_list,
+ *bit_width,
+ *channel_count,
+ channel_count);
+ usb_get_best_match_for_sample_rate(dev_list,
+ *bit_width,
+ *channel_count,
+ *sample_rate,
+ sample_rate);
+exit:
+ ALOGV("%s: Updated sample rate per profile: bit-width(%d) rate(%d) chs(%d)",
+ __func__, *bit_width, *sample_rate, *channel_count);
+ return true;
+}
+
+static int usb_get_sidetone_gain(struct usb_card_config *card_info)
+{
+ int gain = card_info->usb_sidetone_vol_min + usbmod->sidetone_gain;
+ if (gain > card_info->usb_sidetone_vol_max)
+ gain = card_info->usb_sidetone_vol_max;
+ return gain;
+}
+
+void audio_extn_usb_set_sidetone_gain(struct str_parms *parms,
+ char *value, int len)
+{
+ int err;
+
+ err = str_parms_get_str(parms, USB_SIDETONE_GAIN_STR,
+ value, len);
+ if (err >= 0) {
+ usb_sidetone_gain = pow(10.0, (float)(atoi(value))/10.0);
+ ALOGV("%s: sidetone gain(%s) decimal %d",
+ __func__, value, usb_sidetone_gain);
+ str_parms_del(parms, USB_SIDETONE_GAIN_STR);
+ }
+ return;
+}
+
+int audio_extn_usb_enable_sidetone(int device, bool enable)
+{
+ int ret = -ENODEV;
+ struct listnode *node_i;
+ struct usb_card_config *card_info;
+ int i;
+ ALOGV("%s: card_dev_type (0x%x), sidetone enable(%d)",
+ __func__, device, enable);
+
+ list_for_each(node_i, &usbmod->usb_card_conf_list) {
+ card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGV("%s: card_dev_type (0x%x), card_no(%d)",
+ __func__, card_info->usb_device_type, card_info->usb_card);
+ if (card_info->usb_device_type == AUDIO_DEVICE_OUT_USB_DEVICE) {
+ if ((i = card_info->usb_sidetone_index[USB_SIDETONE_ENABLE_INDEX]) != -1) {
+ struct mixer_ctl *ctl = mixer_get_ctl_by_name(
+ card_info->usb_snd_mixer,
+ usb_sidetone_enable_str[i]);
+ if (ctl)
+ mixer_ctl_set_value(ctl, 0, enable);
+ else
+ break;
+
+ if ((i = card_info->usb_sidetone_index[USB_SIDETONE_VOLUME_INDEX]) != -1) {
+ ctl = mixer_get_ctl_by_name(
+ card_info->usb_snd_mixer,
+ usb_sidetone_volume_str[i]);
+ if (ctl == NULL)
+ ALOGV("%s: sidetone gain mixer command is not found",
+ __func__);
+ else if (enable)
+ mixer_ctl_set_value(ctl, 0,
+ usb_get_sidetone_gain(card_info));
+ }
+ ret = 0;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+bool audio_extn_usb_is_config_supported(unsigned int *bit_width,
+ unsigned int *sample_rate,
+ unsigned int *channel_count,
+ bool is_playback)
+{
+ struct listnode *node_i;
+ struct usb_card_config *card_info;
+
+ ALOGV("%s: from stream: bit-width(%d) sample_rate(%d) ch(%d) is_playback(%d)",
+ __func__, *bit_width, *sample_rate, *channel_count, is_playback);
+ list_for_each(node_i, &usbmod->usb_card_conf_list) {
+ card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: card_dev_type (0x%x), card_no(%d)",
+ __func__, card_info->usb_device_type, card_info->usb_card);
+ /* Currently only apply the first playback sound card configuration */
+ if ((is_playback && card_info->usb_device_type == AUDIO_DEVICE_OUT_USB_DEVICE) ||
+ ((!is_playback) && card_info->usb_device_type == AUDIO_DEVICE_IN_USB_DEVICE)){
+ usb_audio_backend_apply_policy(&card_info->usb_device_conf_list,
+ bit_width,
+ sample_rate,
+ channel_count);
+ break;
+ }
+ }
+ ALOGV("%s: updated: bit-width(%d) sample_rate(%d) channels (%d)",
+ __func__, *bit_width, *sample_rate, *channel_count);
+
+ return true;
+}
+
+bool audio_extn_usb_is_capture_supported()
+{
+ if (usbmod == NULL) {
+ ALOGE("%s: USB device object is NULL", __func__);
+ return false;
+ }
+ ALOGV("%s: capture_supported %d",__func__,usbmod->is_capture_supported);
+ return usbmod->is_capture_supported;
+}
+
+void audio_extn_usb_add_device(audio_devices_t device, int card)
+{
+ struct usb_card_config *usb_card_info;
+ char check_debug_enable[PROPERTY_VALUE_MAX];
+ struct listnode *node_i;
+
+ property_get("audio.usb.enable.debug", check_debug_enable, NULL);
+ if (atoi(check_debug_enable)) {
+ usb_audio_debug_enable = true;
+ }
+
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: parameters device(0x%x), card(%d)",
+ __func__, device, card);
+ if (usbmod == NULL) {
+ ALOGE("%s: USB device object is NULL", __func__);
+ goto exit;
+ }
+
+ if (!(usb_valid_device(device)) || (card < 0)) {
+ ALOGE("%s:device(0x%x), card(%d)",
+ __func__, device, card);
+ goto exit;
+ }
+
+ list_for_each(node_i, &usbmod->usb_card_conf_list) {
+ usb_card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGI_IF(usb_audio_debug_enable,
+ "%s: list has capability for card_dev_type (0x%x), card_no(%d)",
+ __func__, usb_card_info->usb_device_type, usb_card_info->usb_card);
+ /* If we have cached the capability */
+ if ((usb_card_info->usb_device_type == device) && (usb_card_info->usb_card == card)) {
+ ALOGV("%s: capability for device(0x%x), card(%d) is cached, no need to update",
+ __func__, device, card);
+ goto exit;
+ }
+ }
+ usb_card_info = calloc(1, sizeof(struct usb_card_config));
+ if (usb_card_info == NULL) {
+ ALOGE("%s: error unable to allocate memory",
+ __func__);
+ goto exit;
+ }
+ list_init(&usb_card_info->usb_device_conf_list);
+ if (device & AUDIO_DEVICE_OUT_USB_DEVICE) {
+ if (!usb_get_device_playback_config(usb_card_info, card)){
+ usb_card_info->usb_card = card;
+ usb_card_info->usb_device_type = AUDIO_DEVICE_OUT_USB_DEVICE;
+ usb_get_sidetone_mixer(usb_card_info);
+ list_add_tail(&usbmod->usb_card_conf_list, &usb_card_info->list);
+ goto exit;
+ }
+ } else if (device & AUDIO_DEVICE_IN_USB_DEVICE) {
+ if (!usb_get_device_capture_config(usb_card_info, card)) {
+ usb_card_info->usb_card = card;
+ usb_card_info->usb_device_type = AUDIO_DEVICE_IN_USB_DEVICE;
+ usbmod->is_capture_supported = true;
+ list_add_tail(&usbmod->usb_card_conf_list, &usb_card_info->list);
+ goto exit;
+ }
+ }
+ /* free memory in error case */
+ if (usb_card_info != NULL)
+ free(usb_card_info);
+exit:
+ if (usb_audio_debug_enable)
+ usb_print_active_device();
+ return;
+}
+
+void audio_extn_usb_remove_device(audio_devices_t device, int card)
+{
+ struct listnode *node_i, *temp_i;
+ struct listnode *node_j, *temp_j;
+ struct usb_device_config *dev_info;
+ struct usb_card_config *card_info;
+ unsigned int i;
+
+ ALOGV("%s: device(0x%x), card(%d)",
+ __func__, device, card);
+
+ if (usbmod == NULL) {
+ ALOGE("%s: USB device object is NULL", __func__);
+ goto exit;
+ }
+
+ if (!(usb_valid_device(device)) || (card < 0)) {
+ ALOGE("%s: Invalid parameters device(0x%x), card(%d)",
+ __func__, device, card);
+ goto exit;
+ }
+ list_for_each_safe(node_i, temp_i, &usbmod->usb_card_conf_list) {
+ card_info = node_to_item(node_i, struct usb_card_config, list);
+ ALOGV("%s: card_dev_type (0x%x), card_no(%d)",
+ __func__, card_info->usb_device_type, card_info->usb_card);
+ if ((device == card_info->usb_device_type) && (card == card_info->usb_card)){
+ list_for_each_safe(node_j, temp_j, &card_info->usb_device_conf_list) {
+ dev_info = node_to_item(node_j, struct usb_device_config, list);
+ ALOGV("%s: bit-width(%d) channel(%d)",
+ __func__, dev_info->bit_width, dev_info->channel_count);
+ for (i = 0; i < dev_info->rate_size; i++)
+ ALOGV("%s: rate %d", __func__, dev_info->rates[i]);
+
+ list_remove(node_j);
+ free(node_to_item(node_j, struct usb_device_config, list));
+ }
+ list_remove(node_i);
+ free(node_to_item(node_i, struct usb_card_config, list));
+ }
+ }
+ usbmod->is_capture_supported = false;
+exit:
+ if (usb_audio_debug_enable)
+ usb_print_active_device();
+
+ return;
+}
+
+void audio_extn_usb_init(void *adev)
+{
+ if (usbmod == NULL) {
+ usbmod = calloc(1, sizeof(struct usb_module));
+ if (usbmod == NULL) {
+ ALOGE("%s: error unable to allocate memory", __func__);
+ goto exit;
+ }
+ } else {
+ memset(usbmod, 0, sizeof(*usbmod));
+ }
+
+ list_init(&usbmod->usb_card_conf_list);
+ usbmod->adev = (struct audio_device*)adev;
+ usbmod->sidetone_gain = usb_sidetone_gain;
+ usbmod->is_capture_supported = false;
+exit:
+ return;
+}
+
+void audio_extn_usb_deinit(void)
+{
+ if (NULL != usbmod){
+ free(usbmod);
+ usbmod = NULL;
+ }
+}
+#endif /*USB_HEADSET_ENABLED end*/
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 0a5bccb..6f0c9c9 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -33,6 +33,7 @@
#include <dlfcn.h>
#include <sys/resource.h>
#include <sys/prctl.h>
+#include <limits.h>
#include <cutils/log.h>
#include <cutils/trace.h>
@@ -44,8 +45,11 @@
#include <hardware/audio_effect.h>
#include <hardware/audio_alsaops.h>
#include <system/thread_defs.h>
+#include <tinyalsa/asoundlib.h>
#include <audio_effects/effect_aec.h>
#include <audio_effects/effect_ns.h>
+#include <audio_utils/clock.h>
+#include <audio_utils/power.h>
#include "audio_hw.h"
#include "audio_extn.h"
#include "platform_api.h"
@@ -83,6 +87,13 @@
static unsigned int configured_low_latency_capture_period_size =
LOW_LATENCY_CAPTURE_PERIOD_SIZE;
+
+#define MMAP_PERIOD_SIZE (DEFAULT_OUTPUT_SAMPLING_RATE/1000)
+#define MMAP_PERIOD_COUNT_MIN 32
+#define MMAP_PERIOD_COUNT_MAX 512
+#define MMAP_PERIOD_COUNT_DEFAULT (MMAP_PERIOD_COUNT_MAX)
+
+
/* This constant enables extended precision handling.
* TODO The flag is off until more testing is done.
*/
@@ -135,6 +146,19 @@
.avail_min = 0,
};
+struct pcm_config pcm_config_mmap_playback = {
+ .channels = DEFAULT_CHANNEL_COUNT,
+ .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
+ .period_size = MMAP_PERIOD_SIZE,
+ .period_count = MMAP_PERIOD_COUNT_DEFAULT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = MMAP_PERIOD_SIZE*8,
+ .stop_threshold = INT32_MAX,
+ .silence_threshold = 0,
+ .silence_size = 0,
+ .avail_min = MMAP_PERIOD_SIZE, //1 ms
+};
+
struct pcm_config pcm_config_audio_capture = {
.channels = DEFAULT_CHANNEL_COUNT,
.period_count = AUDIO_CAPTURE_PERIOD_COUNT,
@@ -156,6 +180,19 @@
.avail_min = ULL_PERIOD_SIZE, //1 ms
};
+struct pcm_config pcm_config_mmap_capture = {
+ .channels = DEFAULT_CHANNEL_COUNT,
+ .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
+ .period_size = MMAP_PERIOD_SIZE,
+ .period_count = MMAP_PERIOD_COUNT_DEFAULT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = 0,
+ .stop_threshold = INT_MAX,
+ .silence_threshold = 0,
+ .silence_size = 0,
+ .avail_min = MMAP_PERIOD_SIZE, //1 ms
+};
+
#define AFE_PROXY_CHANNEL_COUNT 2
#define AFE_PROXY_SAMPLING_RATE 48000
@@ -194,9 +231,11 @@
[USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback",
[USECASE_AUDIO_PLAYBACK_TTS] = "audio-tts-playback",
[USECASE_AUDIO_PLAYBACK_ULL] = "audio-ull-playback",
+ [USECASE_AUDIO_PLAYBACK_MMAP] = "mmap-playback",
[USECASE_AUDIO_RECORD] = "audio-record",
[USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
+ [USECASE_AUDIO_RECORD_MMAP] = "mmap-record",
[USECASE_AUDIO_HFP_SCO] = "hfp-sco",
[USECASE_AUDIO_HFP_SCO_WB] = "hfp-sco-wb",
@@ -232,11 +271,19 @@
static int set_voice_volume_l(struct audio_device *adev, float volume);
static struct audio_device *adev = NULL;
-static pthread_mutex_t adev_init_lock;
+static pthread_mutex_t adev_init_lock = PTHREAD_MUTEX_INITIALIZER;
static unsigned int audio_device_ref_count;
//cache last MBDRC cal step level
static int last_known_cal_step = -1 ;
+// TODO: Consider moving this to a pthread_once() if we have more
+// static initialization required.
+static bool is_userdebug_or_eng_build() {
+ char value[PROPERTY_VALUE_MAX];
+ (void)property_get("ro.build.type", value, "unknown"); // ignore actual length
+ return strcmp(value, "userdebug") == 0 || strcmp(value, "eng") == 0;
+}
+
static bool may_use_noirq_mode(struct audio_device *adev, audio_usecase_t uc_id,
int flags __unused)
{
@@ -306,12 +353,6 @@
{
struct audio_device *adev = out->dev;
- if (out->routing_change) {
- out->routing_change = false;
- if (adev->adm_on_routing_change)
- adev->adm_on_routing_change(adev->adm_data, out->handle);
- }
-
if (adev->adm_request_focus_v2) {
adev->adm_request_focus_v2(adev->adm_data, out->handle, ns);
} else if (adev->adm_request_focus) {
@@ -323,12 +364,6 @@
{
struct audio_device *adev = in->dev;
- if (in->routing_change) {
- in->routing_change = false;
- if (adev->adm_on_routing_change)
- adev->adm_on_routing_change(adev->adm_data, in->capture_handle);
- }
-
if (adev->adm_request_focus_v2) {
adev->adm_request_focus_v2(adev->adm_data, in->capture_handle, ns);
} else if (adev->adm_request_focus) {
@@ -351,79 +386,6 @@
adev->adm_abandon_focus(adev->adm_data, in->capture_handle);
}
-// Time string format similar to logcat, buffer_length must be >= 19 chars.
-static void ns2string(int64_t ns, char *buffer, int buffer_length)
-{
- const int one_second = 1000000000;
- const time_t sec = ns / one_second;
- struct tm tm;
- localtime_r(&sec, &tm);
- snprintf(buffer, buffer_length, "%02d-%02d %02d:%02d:%02d.%03d",
- tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
- tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
- (int)(ns % one_second / 1000000));
-}
-
-// Convert timespec to nsec.
-static int64_t ts2ns(const struct timespec *ts)
-{
- return ts->tv_sec * 1000000000LL + ts->tv_nsec;
-}
-
-// Log errors: consecutive errors with the same code will
-// be aggregated if they occur within one second.
-// A mutual exclusion lock must be held before calling.
-static void log_error_l(struct error_log *log, int code) {
- ++log->errors;
-
- struct timespec now_ts = { 0, 0 };
- (void)clock_gettime(CLOCK_REALTIME, &now_ts);
- const int64_t now = ts2ns(&now_ts);
-
- // Within 1 second, cluster the same error codes together.
- const int one_second = 1000000000;
- if (code == log->entries[log->idx].code &&
- now - log->entries[log->idx].last_time < one_second) {
- log->entries[log->idx].count++;
- log->entries[log->idx].last_time = now;
- return;
- }
-
- // Add new error entry.
- if (++log->idx >= ARRAY_SIZE(log->entries)) {
- log->idx = 0;
- }
- log->entries[log->idx].count = 1;
- log->entries[log->idx].code = code;
- log->entries[log->idx].first_time = now;
- log->entries[log->idx].last_time = now;
-}
-
-// Dump information in the error log. A mutual exclusion lock
-// should be held, but if that cannot be obtained, one should
-// make a copy of the error log before calling -- the call is
-// still safe, but there might be some misinterpreted data.
-static void log_dump_l(const struct error_log *log, int fd)
-{
- dprintf(fd, " Errors: %u\n", log->errors);
- if (log->errors == 0)
- return;
-
- dprintf(fd, " Index Code Freq First time Last time\n");
- for (size_t i = 0; i < ARRAY_SIZE(log->entries); ++i) {
- if (log->entries[i].count != 0) {
- char first_time[32];
- char last_time[32];
- ns2string(log->entries[i].first_time, first_time, sizeof(first_time));
- ns2string(log->entries[i].last_time, last_time, sizeof(last_time));
- dprintf(fd, " %c%4zu %4d %5d %s %s\n",
- i == log->idx ? '*' : ' ', // mark head position
- i, log->entries[i].code, log->entries[i].count,
- first_time, last_time);
- }
- }
-}
-
static int parse_snd_card_status(struct str_parms * parms, int * card,
card_status_t * status)
{
@@ -822,6 +784,8 @@
bool switch_device[AUDIO_USECASE_MAX];
int i, num_uc_to_switch = 0;
+ platform_check_and_set_playback_backend_cfg(adev, uc_info, snd_device);
+
/*
* This function is to make sure that all the usecases that are active on
* the hardware codec backend are always routed to any one device that is
@@ -1166,7 +1130,8 @@
/* Enable new sound devices */
if (out_snd_device != SND_DEVICE_NONE) {
- if (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND)
+ if ((usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) ||
+ (usecase->devices & AUDIO_DEVICE_OUT_USB_DEVICE))
check_and_route_playback_usecases(adev, usecase, out_snd_device);
enable_snd_device(adev, out_snd_device);
}
@@ -1276,60 +1241,72 @@
select_devices(adev, in->usecase);
- ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d",
- __func__, adev->snd_card, in->pcm_device_id, in->config.channels);
-
- unsigned int flags = PCM_IN | PCM_MONOTONIC;
- unsigned int pcm_open_retry_count = 0;
-
- if (in->usecase == USECASE_AUDIO_RECORD_AFE_PROXY) {
- flags |= PCM_MMAP | PCM_NOIRQ;
- pcm_open_retry_count = PROXY_OPEN_RETRY_COUNT;
- } else if (in->realtime) {
- flags |= PCM_MMAP | PCM_NOIRQ;
- }
-
- while (1) {
- in->pcm = pcm_open(adev->snd_card, in->pcm_device_id,
- flags, &in->config);
+ if (in->usecase == USECASE_AUDIO_RECORD_MMAP) {
if (in->pcm == NULL || !pcm_is_ready(in->pcm)) {
- ALOGE("%s: %s", __func__, pcm_get_error(in->pcm));
- if (in->pcm != NULL) {
- pcm_close(in->pcm);
- in->pcm = NULL;
- }
- if (pcm_open_retry_count-- == 0) {
- ret = -EIO;
- goto error_open;
- }
- usleep(PROXY_OPEN_WAIT_TIME * 1000);
- continue;
+ ALOGE("%s: pcm stream not ready", __func__);
+ goto error_open;
}
- break;
- }
-
- ALOGV("%s: pcm_prepare", __func__);
- ret = pcm_prepare(in->pcm);
- if (ret < 0) {
- ALOGE("%s: pcm_prepare returned %d", __func__, ret);
- pcm_close(in->pcm);
- in->pcm = NULL;
- goto error_open;
- }
- if (in->realtime) {
ret = pcm_start(in->pcm);
if (ret < 0) {
- ALOGE("%s: RT pcm_start failed ret %d", __func__, ret);
+ ALOGE("%s: MMAP pcm_start failed ret %d", __func__, ret);
+ goto error_open;
+ }
+ } else {
+ unsigned int flags = PCM_IN | PCM_MONOTONIC;
+ unsigned int pcm_open_retry_count = 0;
+
+ if (in->usecase == USECASE_AUDIO_RECORD_AFE_PROXY) {
+ flags |= PCM_MMAP | PCM_NOIRQ;
+ pcm_open_retry_count = PROXY_OPEN_RETRY_COUNT;
+ } else if (in->realtime) {
+ flags |= PCM_MMAP | PCM_NOIRQ;
+ }
+
+ ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d",
+ __func__, adev->snd_card, in->pcm_device_id, in->config.channels);
+
+ while (1) {
+ in->pcm = pcm_open(adev->snd_card, in->pcm_device_id,
+ flags, &in->config);
+ if (in->pcm == NULL || !pcm_is_ready(in->pcm)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(in->pcm));
+ if (in->pcm != NULL) {
+ pcm_close(in->pcm);
+ in->pcm = NULL;
+ }
+ if (pcm_open_retry_count-- == 0) {
+ ret = -EIO;
+ goto error_open;
+ }
+ usleep(PROXY_OPEN_WAIT_TIME * 1000);
+ continue;
+ }
+ break;
+ }
+
+ ALOGV("%s: pcm_prepare", __func__);
+ ret = pcm_prepare(in->pcm);
+ if (ret < 0) {
+ ALOGE("%s: pcm_prepare returned %d", __func__, ret);
pcm_close(in->pcm);
in->pcm = NULL;
goto error_open;
}
+ if (in->realtime) {
+ ret = pcm_start(in->pcm);
+ if (ret < 0) {
+ ALOGE("%s: RT pcm_start failed ret %d", __func__, ret);
+ pcm_close(in->pcm);
+ in->pcm = NULL;
+ goto error_open;
+ }
+ }
}
register_in_stream(in);
audio_extn_perf_lock_release();
ALOGV("%s: exit", __func__);
- return ret;
+ return 0;
error_open:
stop_input_stream(in);
@@ -1667,8 +1644,36 @@
ALOGV("%s: Opening PCM device card_id(%d) device_id(%d) format(%#x)",
__func__, adev->snd_card, out->pcm_device_id, out->config.format);
- if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) {
- unsigned int flags = PCM_OUT;
+ if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
+ out->pcm = NULL;
+ out->compr = compress_open(adev->snd_card, out->pcm_device_id,
+ COMPRESS_IN, &out->compr_config);
+ if (out->compr && !is_compress_ready(out->compr)) {
+ ALOGE("%s: %s", __func__, compress_get_error(out->compr));
+ compress_close(out->compr);
+ out->compr = NULL;
+ ret = -EIO;
+ goto error_open;
+ }
+ if (out->offload_callback)
+ compress_nonblock(out->compr, out->non_blocking);
+
+ if (adev->visualizer_start_output != NULL)
+ adev->visualizer_start_output(out->handle, out->pcm_device_id);
+ if (adev->offload_effects_start_output != NULL)
+ adev->offload_effects_start_output(out->handle, out->pcm_device_id);
+ } else if (out->usecase == USECASE_AUDIO_PLAYBACK_MMAP) {
+ if (out->pcm == NULL || !pcm_is_ready(out->pcm)) {
+ ALOGE("%s: pcm stream not ready", __func__);
+ goto error_open;
+ }
+ ret = pcm_start(out->pcm);
+ if (ret < 0) {
+ ALOGE("%s: MMAP pcm_start failed ret %d", __func__, ret);
+ goto error_open;
+ }
+ } else {
+ unsigned int flags = PCM_OUT | PCM_MONOTONIC;
unsigned int pcm_open_retry_count = 0;
if (out->usecase == USECASE_AUDIO_PLAYBACK_AFE_PROXY) {
@@ -1676,8 +1681,7 @@
pcm_open_retry_count = PROXY_OPEN_RETRY_COUNT;
} else if (out->realtime) {
flags |= PCM_MMAP | PCM_NOIRQ;
- } else
- flags |= PCM_MONOTONIC;
+ }
while (1) {
out->pcm = pcm_open(adev->snd_card, out->pcm_device_id,
@@ -1707,33 +1711,14 @@
goto error_open;
}
}
- } else {
- out->pcm = NULL;
- out->compr = compress_open(adev->snd_card, out->pcm_device_id,
- COMPRESS_IN, &out->compr_config);
- if (out->compr && !is_compress_ready(out->compr)) {
- ALOGE("%s: %s", __func__, compress_get_error(out->compr));
- compress_close(out->compr);
- out->compr = NULL;
- ret = -EIO;
- goto error_open;
- }
- if (out->offload_callback)
- compress_nonblock(out->compr, out->non_blocking);
-
- if (adev->visualizer_start_output != NULL)
- adev->visualizer_start_output(out->handle, out->pcm_device_id);
- if (adev->offload_effects_start_output != NULL)
- adev->offload_effects_start_output(out->handle, out->pcm_device_id);
- }
- ret = 0;
- if (out->realtime) {
- ret = pcm_start(out->pcm);
- if (ret < 0) {
- ALOGE("%s: RT pcm_start failed ret %d", __func__, ret);
- pcm_close(out->pcm);
- out->pcm = NULL;
- goto error_open;
+ if (out->realtime) {
+ ret = pcm_start(out->pcm);
+ if (ret < 0) {
+ ALOGE("%s: RT pcm_start failed ret %d", __func__, ret);
+ pcm_close(out->pcm);
+ out->pcm = NULL;
+ goto error_open;
+ }
}
}
register_out_stream(out);
@@ -1741,7 +1726,7 @@
audio_extn_tfa_98xx_enable_speaker();
ALOGV("%s: exit", __func__);
- return ret;
+ return 0;
error_open:
audio_extn_perf_lock_release();
stop_output_stream(out);
@@ -1856,6 +1841,7 @@
{
struct stream_out *out = (struct stream_out *)stream;
struct audio_device *adev = out->dev;
+ bool do_stop = true;
ALOGV("%s: enter: usecase(%d: %s)", __func__,
out->usecase, use_case_table[out->usecase]);
@@ -1864,7 +1850,6 @@
if (!out->standby) {
if (adev->adm_deregister_stream)
adev->adm_deregister_stream(adev->adm_data, out->handle);
-
pthread_mutex_lock(&adev->lock);
out->standby = true;
if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) {
@@ -1872,6 +1857,10 @@
pcm_close(out->pcm);
out->pcm = NULL;
}
+ if (out->usecase == USECASE_AUDIO_PLAYBACK_MMAP) {
+ do_stop = out->playback_started;
+ out->playback_started = false;
+ }
} else {
stop_compressed_output_l(out);
out->gapless_mdata.encoder_delay = 0;
@@ -1881,7 +1870,9 @@
out->compr = NULL;
}
}
- stop_output_stream(out);
+ if (do_stop) {
+ stop_output_stream(out);
+ }
pthread_mutex_unlock(&adev->lock);
}
pthread_mutex_unlock(&out->lock);
@@ -1923,13 +1914,15 @@
dprintf(fd, " Frames written: %lld\n", (long long)out->written);
if (locked) {
- log_dump_l(&out->error_log, fd);
pthread_mutex_unlock(&out->lock);
- } else {
- // We don't have the lock here, copy for safety.
- struct error_log log = out->error_log;
- log_dump_l(&log, fd);
}
+
+ // dump error info
+ (void)error_log_dump(
+ out->error_log, fd, " " /* prefix */, 0 /* lines */, 0 /* limit_ns */);
+ // dump power info (out->power_log may be null)
+ (void)power_log_dump(
+ out->power_log, fd, " " /* prefix */, POWER_LOG_LINES, 0 /* limit_ns */);
return 0;
}
@@ -2041,7 +2034,11 @@
if (!out->standby) {
if (!same_dev) {
ALOGV("update routing change");
- out->routing_change = true;
+ // inform adm before actual routing to prevent glitches.
+ if (adev->adm_on_routing_change) {
+ adev->adm_on_routing_change(adev->adm_data,
+ out->handle);
+ }
}
select_devices(adev, out->usecase);
audio_extn_tfa_98xx_update();
@@ -2096,7 +2093,7 @@
str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value);
str = str_parms_to_str(reply);
} else {
- str = strdup(keys);
+ str = strdup("");
}
str_parms_destroy(query);
str_parms_destroy(reply);
@@ -2111,7 +2108,8 @@
if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD)
return COMPRESS_OFFLOAD_PLAYBACK_LATENCY;
- else if (out->realtime) {
+ else if ((out->realtime) ||
+ (out->usecase == USECASE_AUDIO_PLAYBACK_MMAP)) {
// since the buffer won't be filled up faster than realtime,
// return a smaller number
period_ms = (out->af_period_multiplier * out->config.period_size *
@@ -2200,39 +2198,18 @@
#ifdef NO_AUDIO_OUT
static ssize_t out_write_for_no_output(struct audio_stream_out *stream,
- const void *buffer, size_t bytes)
+ const void *buffer __unused, size_t bytes)
{
struct stream_out *out = (struct stream_out *)stream;
- struct timespec t = { .tv_sec = 0, .tv_nsec = 0 };
- int64_t now;
- int64_t elapsed_time_since_last_write = 0;
- int64_t sleep_time;
- clock_gettime(CLOCK_MONOTONIC, &t);
- now = (t.tv_sec * 1000000000LL + t.tv_nsec) / 1000;
-
+ /* No Output device supported other than BT for playback.
+ * Sleep for the amount of buffer duration
+ */
lock_output_stream(out);
- if (out->last_write_time_us)
- elapsed_time_since_last_write = now - out->last_write_time_us;
- sleep_time = bytes * 1000000LL / audio_stream_out_frame_size(stream) /
- out_get_sample_rate(&stream->common) - elapsed_time_since_last_write;
- if (sleep_time > 0) {
- usleep(sleep_time);
- } else {
- // we don't sleep when we exit standby (this is typical for a real alsa buffer).
- sleep_time = 0;
- }
- out->last_write_time_us = now + sleep_time;
+ usleep(bytes * 1000000 / audio_stream_out_frame_size(
+ (const struct audio_stream_out *)&out->stream) /
+ out_get_sample_rate(&out->stream.common));
pthread_mutex_unlock(&out->lock);
- // last_write_time_us is an approximation of when the (simulated) alsa
- // buffer is believed completely full. The usleep above waits for more space
- // in the buffer, but by the end of the sleep the buffer is considered
- // topped-off.
- //
- // On the subsequent out_write(), we measure the elapsed time spent in
- // the mixer. This is subtracted from the sleep estimate based on frames,
- // thereby accounting for drain in the alsa buffer during mixing.
- // This is a crude approximation; we don't handle underruns precisely.
return bytes;
}
#endif
@@ -2246,6 +2223,13 @@
int error_code = ERROR_CODE_STANDBY;
lock_output_stream(out);
+ // this is always nonzero
+ const int frame_size = audio_stream_out_frame_size(stream);
+
+ if (out->usecase == USECASE_AUDIO_PLAYBACK_MMAP) {
+ error_code = ERROR_CODE_WRITE;
+ goto exit;
+ }
if (out->standby) {
out->standby = false;
pthread_mutex_lock(&adev->lock);
@@ -2264,7 +2248,7 @@
}
if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
- ALOGVV("%s: writing buffer (%d bytes) to compress device", __func__, bytes);
+ ALOGVV("%s: writing buffer (%zu bytes) to compress device", __func__, bytes);
if (out->send_new_metadata) {
ALOGVV("send new gapless metadata");
compress_set_gapless_metadata(out->compr, &out->gapless_mdata);
@@ -2297,9 +2281,12 @@
out->offload_state = OFFLOAD_STATE_PLAYING;
}
if (ret < 0) {
- log_error_l(&out->error_log, ERROR_CODE_WRITE);
+ error_log_log(out->error_log, ERROR_CODE_WRITE, audio_utils_get_real_time_ns());
+ } else {
+ out->written += ret; // accumulate bytes written for offload.
}
pthread_mutex_unlock(&out->lock);
+ // TODO: consider logging offload pcm
return ret;
} else {
error_code = ERROR_CODE_WRITE;
@@ -2307,7 +2294,7 @@
if (out->muted)
memset((void *)buffer, 0, bytes);
- ALOGVV("%s: writing buffer (%d bytes) to pcm device", __func__, bytes);
+ ALOGVV("%s: writing buffer (%zu bytes) to pcm device", __func__, bytes);
long ns = pcm_bytes_to_frames(out->pcm, bytes)*1000000000LL/
out->config.rate;
@@ -2331,12 +2318,18 @@
out->written += bytes / (out->config.channels * sizeof(short));
}
long long sleeptime_us = 0;
+
+ // only get time if needed for logging, as it is a system call on 32 bit devices.
+ // TODO: Consider always enabling for 64 bit vDSO using compile time check on __LP64__.
+ const int64_t now_ns = out->power_log != 0 || (ret != 0 && out->error_log != 0)
+ ? audio_utils_get_real_time_ns() : 0;
+
if (ret != 0) {
- log_error_l(&out->error_log, error_code);
+ error_log_log(out->error_log, error_code, now_ns);
if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) {
ALOGE_IF(out->pcm != NULL,
"%s: error %zd - %s", __func__, ret, pcm_get_error(out->pcm));
- sleeptime_us = bytes * 1000000LL / audio_stream_out_frame_size(stream) /
+ sleeptime_us = bytes * 1000000LL / frame_size /
out_get_sample_rate(&out->stream.common);
// usleep not guaranteed for values over 1 second but we don't limit here.
}
@@ -2348,6 +2341,9 @@
out_on_error(&out->stream.common);
if (sleeptime_us != 0)
usleep(sleeptime_us);
+ } else {
+ // only log if the data is properly written (out->power_log may be null)
+ power_log_log(out->power_log, buffer, bytes / frame_size, now_ns);
}
return bytes;
}
@@ -2370,7 +2366,7 @@
pthread_mutex_unlock(&out->lock);
return 0;
} else
- return -EINVAL;
+ return -ENODATA;
}
static int out_add_audio_effect(const struct audio_stream *stream __unused,
@@ -2388,14 +2384,14 @@
static int out_get_next_write_timestamp(const struct audio_stream_out *stream __unused,
int64_t *timestamp __unused)
{
- return -EINVAL;
+ return -ENOSYS;
}
static int out_get_presentation_position(const struct audio_stream_out *stream,
uint64_t *frames, struct timespec *timestamp)
{
struct stream_out *out = (struct stream_out *)stream;
- int ret = -EINVAL;
+ int ret = -ENODATA;
unsigned long dsp_frames;
lock_output_stream(out);
@@ -2512,6 +2508,170 @@
return -ENOSYS;
}
+static int out_stop(const struct audio_stream_out* stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->dev;
+ int ret = -ENOSYS;
+
+ ALOGV("%s", __func__);
+ pthread_mutex_lock(&adev->lock);
+ if (out->usecase == USECASE_AUDIO_PLAYBACK_MMAP && !out->standby &&
+ out->playback_started && out->pcm != NULL) {
+ pcm_stop(out->pcm);
+ ret = stop_output_stream(out);
+ out->playback_started = false;
+ }
+ pthread_mutex_unlock(&adev->lock);
+ return ret;
+}
+
+static int out_start(const struct audio_stream_out* stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->dev;
+ int ret = -ENOSYS;
+
+ ALOGV("%s", __func__);
+ pthread_mutex_lock(&adev->lock);
+ if (out->usecase == USECASE_AUDIO_PLAYBACK_MMAP && !out->standby &&
+ !out->playback_started && out->pcm != NULL) {
+ ret = start_output_stream(out);
+ if (ret == 0) {
+ out->playback_started = true;
+ }
+ }
+ pthread_mutex_unlock(&adev->lock);
+ return ret;
+}
+
+/*
+ * Modify config->period_count based on min_size_frames
+ */
+static void adjust_mmap_period_count(struct pcm_config *config, int32_t min_size_frames)
+{
+ int periodCountRequested = (min_size_frames + config->period_size - 1)
+ / config->period_size;
+ int periodCount = MMAP_PERIOD_COUNT_MIN;
+
+ ALOGV("%s original config.period_size = %d config.period_count = %d",
+ __func__, config->period_size, config->period_count);
+
+ while (periodCount < periodCountRequested && (periodCount * 2) < MMAP_PERIOD_COUNT_MAX) {
+ periodCount *= 2;
+ }
+ config->period_count = periodCount;
+
+ ALOGV("%s requested config.period_count = %d", __func__, config->period_count);
+}
+
+static int out_create_mmap_buffer(const struct audio_stream_out *stream,
+ int32_t min_size_frames,
+ struct audio_mmap_buffer_info *info)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->dev;
+ int ret = 0;
+ unsigned int offset1;
+ unsigned int frames1;
+ const char *step = "";
+
+ ALOGV("%s", __func__);
+ pthread_mutex_lock(&adev->lock);
+
+ if (info == NULL || min_size_frames == 0) {
+ ALOGE("%s: info = %p, min_size_frames = %d", __func__, info, min_size_frames);
+ ret = -EINVAL;
+ goto exit;
+ }
+ if (out->usecase != USECASE_AUDIO_PLAYBACK_MMAP || !out->standby) {
+ ALOGE("%s: usecase = %d, standby = %d", __func__, out->usecase, out->standby);
+ ret = -ENOSYS;
+ goto exit;
+ }
+ out->pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
+ if (out->pcm_device_id < 0) {
+ ALOGE("%s: Invalid PCM device id(%d) for the usecase(%d)",
+ __func__, out->pcm_device_id, out->usecase);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ adjust_mmap_period_count(&out->config, min_size_frames);
+
+ ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d",
+ __func__, adev->snd_card, out->pcm_device_id, out->config.channels);
+ out->pcm = pcm_open(adev->snd_card, out->pcm_device_id,
+ (PCM_OUT | PCM_MMAP | PCM_NOIRQ | PCM_MONOTONIC), &out->config);
+ if (out->pcm == NULL || !pcm_is_ready(out->pcm)) {
+ step = "open";
+ ret = -ENODEV;
+ goto exit;
+ }
+ ret = pcm_mmap_begin(out->pcm, &info->shared_memory_address, &offset1, &frames1);
+ if (ret < 0) {
+ step = "begin";
+ goto exit;
+ }
+ info->buffer_size_frames = pcm_get_buffer_size(out->pcm);
+ info->burst_size_frames = out->config.period_size;
+ info->shared_memory_fd = pcm_get_poll_fd(out->pcm);
+
+ memset(info->shared_memory_address, 0, pcm_frames_to_bytes(out->pcm,
+ info->buffer_size_frames));
+
+ ret = pcm_mmap_commit(out->pcm, 0, MMAP_PERIOD_SIZE);
+ if (ret < 0) {
+ step = "commit";
+ goto exit;
+ }
+
+ out->standby = false;
+ ret = 0;
+
+ ALOGV("%s: got mmap buffer address %p info->buffer_size_frames %d",
+ __func__, info->shared_memory_address, info->buffer_size_frames);
+
+exit:
+ if (ret != 0) {
+ if (out->pcm == NULL) {
+ ALOGE("%s: %s - %d", __func__, step, ret);
+ } else {
+ ALOGE("%s: %s %s", __func__, step, pcm_get_error(out->pcm));
+ pcm_close(out->pcm);
+ out->pcm = NULL;
+ }
+ }
+ pthread_mutex_unlock(&adev->lock);
+ return ret;
+}
+
+static int out_get_mmap_position(const struct audio_stream_out *stream,
+ struct audio_mmap_position *position)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ ALOGVV("%s", __func__);
+ if (position == NULL) {
+ return -EINVAL;
+ }
+ if (out->usecase != USECASE_AUDIO_PLAYBACK_MMAP) {
+ return -ENOSYS;
+ }
+ if (out->pcm == NULL) {
+ return -ENOSYS;
+ }
+
+ struct timespec ts = { 0, 0 };
+ int ret = pcm_mmap_get_hw_ptr(out->pcm, (unsigned int *)&position->position_frames, &ts);
+ if (ret < 0) {
+ ALOGE("%s: %s", __func__, pcm_get_error(out->pcm));
+ return ret;
+ }
+ position->time_nanoseconds = audio_utils_ns_from_timespec(&ts);
+ return 0;
+}
+
+
/** audio_stream_in implementation **/
static uint32_t in_get_sample_rate(const struct audio_stream *stream)
{
@@ -2556,6 +2716,8 @@
struct stream_in *in = (struct stream_in *)stream;
struct audio_device *adev = in->dev;
int status = 0;
+ bool do_stop = true;
+
ALOGV("%s: enter", __func__);
lock_input_stream(in);
@@ -2572,13 +2734,19 @@
pthread_mutex_lock(&adev->lock);
in->standby = true;
+ if (in->usecase == USECASE_AUDIO_RECORD_MMAP) {
+ do_stop = in->capture_started;
+ in->capture_started = false;
+ }
if (in->pcm) {
pcm_close(in->pcm);
in->pcm = NULL;
}
adev->enable_voicerx = false;
platform_set_echo_reference(adev, false, AUDIO_DEVICE_NONE );
- status = stop_input_stream(in);
+ if (do_stop) {
+ status = stop_input_stream(in);
+ }
pthread_mutex_unlock(&adev->lock);
}
pthread_mutex_unlock(&in->lock);
@@ -2626,7 +2794,11 @@
/* If recording is in progress, change the tx device to new device */
if (!in->standby) {
ALOGV("update input routing change");
- in->routing_change = true;
+ // inform adm before actual routing to prevent glitches.
+ if (adev->adm_on_routing_change) {
+ adev->adm_on_routing_change(adev->adm_data,
+ in->capture_handle);
+ }
select_devices(adev, in->usecase);
}
}
@@ -2648,7 +2820,7 @@
static int in_set_gain(struct audio_stream_in *stream __unused, float gain __unused)
{
- return 0;
+ return -ENOSYS;
}
static void in_snd_mon_cb(void * stream, struct str_parms * parms)
@@ -2699,13 +2871,18 @@
lock_input_stream(in);
if (in->is_st_session) {
- ALOGVV(" %s: reading on st session bytes=%d", __func__, bytes);
+ ALOGVV(" %s: reading on st session bytes=%zu", __func__, bytes);
/* Read from sound trigger HAL */
audio_extn_sound_trigger_read(in, buffer, bytes);
pthread_mutex_unlock(&in->lock);
return bytes;
}
+ if (in->usecase == USECASE_AUDIO_RECORD_MMAP) {
+ ret = -ENOSYS;
+ goto exit;
+ }
+
if (in->standby) {
pthread_mutex_lock(&adev->lock);
ret = start_input_stream(in);
@@ -2866,6 +3043,153 @@
return add_remove_audio_effect(stream, effect, false);
}
+static int in_stop(const struct audio_stream_in* stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->dev;
+
+ int ret = -ENOSYS;
+ ALOGV("%s", __func__);
+ pthread_mutex_lock(&adev->lock);
+ if (in->usecase == USECASE_AUDIO_RECORD_MMAP && !in->standby &&
+ in->capture_started && in->pcm != NULL) {
+ pcm_stop(in->pcm);
+ ret = stop_input_stream(in);
+ in->capture_started = false;
+ }
+ pthread_mutex_unlock(&adev->lock);
+ return ret;
+}
+
+static int in_start(const struct audio_stream_in* stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->dev;
+ int ret = -ENOSYS;
+
+ ALOGV("%s in %p", __func__, in);
+ pthread_mutex_lock(&adev->lock);
+ if (in->usecase == USECASE_AUDIO_RECORD_MMAP && !in->standby &&
+ !in->capture_started && in->pcm != NULL) {
+ if (!in->capture_started) {
+ ret = start_input_stream(in);
+ if (ret == 0) {
+ in->capture_started = true;
+ }
+ }
+ }
+ pthread_mutex_unlock(&adev->lock);
+ return ret;
+}
+
+static int in_create_mmap_buffer(const struct audio_stream_in *stream,
+ int32_t min_size_frames,
+ struct audio_mmap_buffer_info *info)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->dev;
+ int ret = 0;
+ unsigned int offset1;
+ unsigned int frames1;
+ const char *step = "";
+
+ pthread_mutex_lock(&adev->lock);
+ ALOGV("%s in %p", __func__, in);
+
+ if (info == NULL || min_size_frames == 0) {
+ ALOGE("%s invalid argument info %p min_size_frames %d", __func__, info, min_size_frames);
+ ret = -EINVAL;
+ goto exit;
+ }
+ if (in->usecase != USECASE_AUDIO_RECORD_MMAP || !in->standby) {
+ ALOGE("%s: usecase = %d, standby = %d", __func__, in->usecase, in->standby);
+ ALOGV("%s in %p", __func__, in);
+ ret = -ENOSYS;
+ goto exit;
+ }
+ in->pcm_device_id = platform_get_pcm_device_id(in->usecase, PCM_CAPTURE);
+ if (in->pcm_device_id < 0) {
+ ALOGE("%s: Invalid PCM device id(%d) for the usecase(%d)",
+ __func__, in->pcm_device_id, in->usecase);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ adjust_mmap_period_count(&in->config, min_size_frames);
+
+ ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d",
+ __func__, adev->snd_card, in->pcm_device_id, in->config.channels);
+ in->pcm = pcm_open(adev->snd_card, in->pcm_device_id,
+ (PCM_IN | PCM_MMAP | PCM_NOIRQ | PCM_MONOTONIC), &in->config);
+ if (in->pcm == NULL || !pcm_is_ready(in->pcm)) {
+ step = "open";
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ ret = pcm_mmap_begin(in->pcm, &info->shared_memory_address, &offset1, &frames1);
+ if (ret < 0) {
+ step = "begin";
+ goto exit;
+ }
+ info->buffer_size_frames = pcm_get_buffer_size(in->pcm);
+ info->burst_size_frames = in->config.period_size;
+ info->shared_memory_fd = pcm_get_poll_fd(in->pcm);
+
+ memset(info->shared_memory_address, 0, pcm_frames_to_bytes(in->pcm,
+ info->buffer_size_frames));
+
+ ret = pcm_mmap_commit(in->pcm, 0, MMAP_PERIOD_SIZE);
+ if (ret < 0) {
+ step = "commit";
+ goto exit;
+ }
+
+ in->standby = false;
+ ret = 0;
+
+ ALOGV("%s: got mmap buffer address %p info->buffer_size_frames %d",
+ __func__, info->shared_memory_address, info->buffer_size_frames);
+
+exit:
+ if (ret != 0) {
+ if (in->pcm == NULL) {
+ ALOGE("%s: %s - %d", __func__, step, ret);
+ } else {
+ ALOGE("%s: %s %s", __func__, step, pcm_get_error(in->pcm));
+ pcm_close(in->pcm);
+ in->pcm = NULL;
+ }
+ }
+ pthread_mutex_unlock(&adev->lock);
+ return ret;
+}
+
+static int in_get_mmap_position(const struct audio_stream_in *stream,
+ struct audio_mmap_position *position)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ ALOGVV("%s", __func__);
+ if (position == NULL) {
+ return -EINVAL;
+ }
+ if (in->usecase != USECASE_AUDIO_RECORD_MMAP) {
+ return -ENOSYS;
+ }
+ if (in->pcm == NULL) {
+ return -ENOSYS;
+ }
+ struct timespec ts = { 0, 0 };
+ int ret = pcm_mmap_get_hw_ptr(in->pcm, (unsigned int *)&position->position_frames, &ts);
+ if (ret < 0) {
+ ALOGE("%s: %s", __func__, pcm_get_error(in->pcm));
+ return ret;
+ }
+ position->time_nanoseconds = audio_utils_ns_from_timespec(&ts);
+ return 0;
+}
+
+
static int adev_open_output_stream(struct audio_hw_device *dev,
audio_io_handle_t handle,
audio_devices_t devices,
@@ -3012,8 +3336,14 @@
} else if (out->flags & AUDIO_OUTPUT_FLAG_RAW) {
out->usecase = USECASE_AUDIO_PLAYBACK_ULL;
out->realtime = may_use_noirq_mode(adev, USECASE_AUDIO_PLAYBACK_ULL, out->flags);
- out->usecase = USECASE_AUDIO_PLAYBACK_ULL;
out->config = out->realtime ? pcm_config_rt : pcm_config_low_latency;
+ } else if (out->flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
+ out->usecase = USECASE_AUDIO_PLAYBACK_MMAP;
+ out->config = pcm_config_mmap_playback;
+ out->stream.start = out_start;
+ out->stream.stop = out_stop;
+ out->stream.create_mmap_buffer = out_create_mmap_buffer;
+ out->stream.get_mmap_position = out_get_mmap_position;
} else {
out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
out->config = pcm_config_low_latency;
@@ -3081,7 +3411,11 @@
out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
out->stream.get_presentation_position = out_get_presentation_position;
- out->af_period_multiplier = out->realtime ? af_period_multiplier : 1;
+ if (out->realtime)
+ out->af_period_multiplier = af_period_multiplier;
+ else
+ out->af_period_multiplier = 1;
+
out->standby = 1;
/* out->muted = false; by calloc() */
/* out->written = 0; by calloc() */
@@ -3094,6 +3428,23 @@
config->channel_mask = out->stream.common.get_channels(&out->stream.common);
config->sample_rate = out->stream.common.get_sample_rate(&out->stream.common);
+ out->error_log = error_log_create(
+ ERROR_LOG_ENTRIES,
+ 1000000000 /* aggregate consecutive identical errors within one second in ns */);
+
+ // power_log may be null if the format is not supported
+ // or not a userdebug or eng build.
+ if (is_userdebug_or_eng_build()) {
+ const size_t POWER_LOG_FRAMES_PER_ENTRY =
+ (long long)config->sample_rate * POWER_LOG_SAMPLING_INTERVAL_MS / 1000;
+
+ out->power_log = power_log_create(
+ config->sample_rate,
+ audio_channel_count_from_out_mask(config->channel_mask),
+ config->format,
+ POWER_LOG_ENTRIES,
+ POWER_LOG_FRAMES_PER_ENTRY);
+ }
/*
By locking output stream before registering, we allow the callback
@@ -3108,6 +3459,7 @@
pthread_mutex_unlock(&out->lock);
*stream_out = &out->stream;
+
ALOGV("%s: exit", __func__);
return 0;
@@ -3140,6 +3492,12 @@
if (adev->voice_tx_output == out)
adev->voice_tx_output = NULL;
+ power_log_destroy(out->power_log);
+ out->power_log = NULL;
+
+ error_log_destroy(out->error_log);
+ out->error_log = NULL;
+
pthread_cond_destroy(&out->cond);
pthread_mutex_destroy(&out->lock);
free(stream);
@@ -3211,6 +3569,43 @@
adev->bt_wb_speech_enabled = !strcmp(value, AUDIO_PARAMETER_VALUE_ON);
}
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value, sizeof(value));
+ if (ret >= 0) {
+ audio_devices_t device = (audio_devices_t)strtoul(value, NULL, 10);
+ if (device == AUDIO_DEVICE_OUT_USB_DEVICE) {
+ ret = str_parms_get_str(parms, "card", value, sizeof(value));
+ if (ret >= 0) {
+ const int card = atoi(value);
+ audio_extn_usb_add_device(AUDIO_DEVICE_OUT_USB_DEVICE, card);
+ }
+ } else if (device == AUDIO_DEVICE_IN_USB_DEVICE) {
+ ret = str_parms_get_str(parms, "card", value, sizeof(value));
+ if (ret >= 0) {
+ const int card = atoi(value);
+ audio_extn_usb_add_device(AUDIO_DEVICE_IN_USB_DEVICE, card);
+ }
+ }
+ }
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value, sizeof(value));
+ if (ret >= 0) {
+ audio_devices_t device = (audio_devices_t)strtoul(value, NULL, 10);
+ if (device == AUDIO_DEVICE_OUT_USB_DEVICE) {
+ ret = str_parms_get_str(parms, "card", value, sizeof(value));
+ if (ret >= 0) {
+ const int card = atoi(value);
+
+ audio_extn_usb_remove_device(AUDIO_DEVICE_OUT_USB_DEVICE, card);
+ }
+ } else if (device == AUDIO_DEVICE_IN_USB_DEVICE) {
+ ret = str_parms_get_str(parms, "card", value, sizeof(value));
+ if (ret >= 0) {
+ const int card = atoi(value);
+ audio_extn_usb_remove_device(AUDIO_DEVICE_IN_USB_DEVICE, card);
+ }
+ }
+ }
+
audio_extn_hfp_set_parameters(adev, parms);
done:
str_parms_destroy(parms);
@@ -3438,40 +3833,57 @@
in->usecase = USECASE_AUDIO_RECORD_AFE_PROXY;
in->config = pcm_config_afe_proxy_record;
+ in->af_period_multiplier = 1;
} else {
in->usecase = USECASE_AUDIO_RECORD;
if (config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE &&
- (flags & AUDIO_INPUT_FLAG_FAST) != 0) {
+ (in->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->config = in->realtime ? pcm_config_audio_capture_rt :
- pcm_config_audio_capture;
-
- if (config->format == AUDIO_FORMAT_PCM_8_24_BIT)
- in->config.format = PCM_FORMAT_S24_LE;
-
- if (!in->realtime) {
+ if (!in->realtime) {
+ in->config = pcm_config_audio_capture;
+ frame_size = audio_stream_in_frame_size(&in->stream);
+ buffer_size = get_input_buffer_size(config->sample_rate,
+ config->format,
+ channel_count,
+ is_low_latency);
+ in->config.period_size = buffer_size / frame_size;
+ in->config.rate = config->sample_rate;
+ in->af_period_multiplier = 1;
+ } else {
+ // period size is left untouched for rt mode playback
+ in->config = pcm_config_audio_capture_rt;
+ in->af_period_multiplier = af_period_multiplier;
+ }
+ } else if ((config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE) &&
+ ((in->flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0)) {
+ in->usecase = USECASE_AUDIO_RECORD_MMAP;
+ in->config = pcm_config_mmap_capture;
+ in->stream.start = in_start;
+ in->stream.stop = in_stop;
+ in->stream.create_mmap_buffer = in_create_mmap_buffer;
+ in->stream.get_mmap_position = in_get_mmap_position;
+ in->af_period_multiplier = 1;
+ ALOGV("%s: USECASE_AUDIO_RECORD_MMAP", __func__);
+ } else {
+ in->config = pcm_config_audio_capture;
frame_size = audio_stream_in_frame_size(&in->stream);
buffer_size = get_input_buffer_size(config->sample_rate,
config->format,
channel_count,
is_low_latency);
in->config.period_size = buffer_size / frame_size;
- } // period size is left untouched for rt mode playback
+ in->config.rate = config->sample_rate;
+ in->af_period_multiplier = 1;
+ }
+ if (config->format == AUDIO_FORMAT_PCM_8_24_BIT)
+ in->config.format = PCM_FORMAT_S24_LE;
}
in->config.channels = channel_count;
- if (in->realtime) {
- in->af_period_multiplier = af_period_multiplier;
- } else {
- in->config.rate = config->sample_rate;
- in->af_period_multiplier = 1;
- }
/* This stream could be for sound trigger lab,
get sound trigger pcm if present */
@@ -3654,9 +4066,11 @@
if (!adev)
return 0;
+ audio_extn_snd_mon_unregister_listener(adev);
+ audio_extn_snd_mon_deinit();
+
audio_extn_tfa_98xx_deinit();
- audio_extn_snd_mon_unregister_listener(adev);
pthread_mutex_lock(&adev_init_lock);
if ((--audio_device_ref_count) == 0) {
@@ -3762,6 +4176,7 @@
adev->device.open_output_stream = adev_open_output_stream;
adev->device.close_output_stream = adev_close_output_stream;
adev->device.open_input_stream = adev_open_input_stream;
+
adev->device.close_input_stream = adev_close_input_stream;
adev->device.dump = adev_dump;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 2cf72e1..ca068e2 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -25,15 +25,17 @@
#include <tinycompress/tinycompress.h>
#include <audio_route/audio_route.h>
+#include <audio_utils/ErrorLog.h>
+#include <audio_utils/PowerLog.h>
#include "voice.h"
// dlopen() does not go through default library path search if there is a "/" in the library name.
#ifdef __LP64__
-#define VISUALIZER_LIBRARY_PATH "/system/lib64/soundfx/libqcomvisualizer.so"
-#define OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH "/system/lib64/soundfx/libqcompostprocbundle.so"
+#define VISUALIZER_LIBRARY_PATH "/vendor/lib64/soundfx/libqcomvisualizer.so"
+#define OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH "/vendor/lib64/soundfx/libqcompostprocbundle.so"
#else
-#define VISUALIZER_LIBRARY_PATH "/system/lib/soundfx/libqcomvisualizer.so"
-#define OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH "/system/lib/soundfx/libqcompostprocbundle.so"
+#define VISUALIZER_LIBRARY_PATH "/vendor/lib/soundfx/libqcomvisualizer.so"
+#define OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH "/vendor/lib/soundfx/libqcompostprocbundle.so"
#endif
#define ADM_LIBRARY_PATH "libadm.so"
@@ -49,10 +51,23 @@
#define ACDB_DEV_TYPE_IN 2
#define MAX_SUPPORTED_CHANNEL_MASKS 2
+#define MAX_SUPPORTED_FORMATS 15
+#define MAX_SUPPORTED_SAMPLE_RATES 7
#define DEFAULT_HDMI_OUT_CHANNELS 2
#define ERROR_LOG_ENTRIES 16
+#define POWER_LOG_LINES 40
+#define POWER_LOG_SAMPLING_INTERVAL_MS 50
+#define POWER_LOG_ENTRIES (1 /* minutes */ * 60 /* seconds */ * 1000 /* msec */ \
+ / POWER_LOG_SAMPLING_INTERVAL_MS)
+
+/* Error types for the error log */
+enum {
+ ERROR_CODE_STANDBY = 1,
+ ERROR_CODE_WRITE,
+};
+
typedef enum card_status_t {
CARD_STATUS_OFFLINE,
CARD_STATUS_ONLINE
@@ -71,6 +86,7 @@
USECASE_AUDIO_PLAYBACK_OFFLOAD,
USECASE_AUDIO_PLAYBACK_TTS,
USECASE_AUDIO_PLAYBACK_ULL,
+ USECASE_AUDIO_PLAYBACK_MMAP,
/* HFP Use case*/
USECASE_AUDIO_HFP_SCO,
@@ -79,6 +95,7 @@
/* Capture usecases */
USECASE_AUDIO_RECORD,
USECASE_AUDIO_RECORD_LOW_LATENCY,
+ USECASE_AUDIO_RECORD_MMAP,
/* Voice extension usecases
*
@@ -150,24 +167,6 @@
int data[];
};
-enum {
- ERROR_CODE_STANDBY,
- ERROR_CODE_WRITE,
-};
-
-struct error_log_entry {
- int32_t code;
- int32_t count;
- int64_t first_time;
- int64_t last_time;
-};
-
-struct error_log {
- uint32_t errors;
- uint32_t idx;
- struct error_log_entry entries[ERROR_LOG_ENTRIES];
-};
-
struct stream_out {
struct audio_stream_out stream;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
@@ -205,13 +204,11 @@
int send_new_metadata;
bool realtime;
int af_period_multiplier;
- bool routing_change;
struct audio_device *dev;
card_status_t card_status;
- struct error_log error_log;
-
- int64_t last_write_time_us;
+ error_log_t *error_log;
+ power_log_t *power_log;
};
struct stream_in {
@@ -236,10 +233,10 @@
bool is_st_session_active;
bool realtime;
int af_period_multiplier;
- bool routing_change;
struct audio_device *dev;
audio_format_t format;
card_status_t card_status;
+ int capture_started;
};
typedef enum usecase_type_t {
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 4995bd8..2fe6168 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -969,7 +969,7 @@
ALOGE("%s: Could not find the symbol acdb_loader_deallocate_ACDB from %s",
__func__, LIB_ACDB_LOADER);
- my_data->acdb_send_audio_cal_v3 = (acdb_send_audio_cal_t)dlsym(my_data->acdb_handle,
+ my_data->acdb_send_audio_cal_v3 = (acdb_send_audio_cal_v3_t)dlsym(my_data->acdb_handle,
"acdb_loader_send_audio_cal_v3");
if (!my_data->acdb_send_audio_cal_v3)
ALOGE("%s: Could not find the symbol acdb_send_audio_cal from %s",
@@ -1054,6 +1054,7 @@
free(info_item);
}
+ mixer_close(my_data->adev->mixer);
free(platform);
}
@@ -1073,7 +1074,7 @@
{
struct platform_data *my_data = (struct platform_data *)platform;
- if (platform == NULL || device_name == NULL) {
+ if (platform == NULL) {
ALOGW("%s: something wrong, use legacy get_snd_device name", __func__);
strlcpy(device_name, platform_get_snd_device_name(snd_device),
DEVICE_NAME_MAX_SIZE);
@@ -1093,9 +1094,16 @@
return 0;
}
+bool platform_check_and_set_playback_backend_cfg(struct audio_device* adev __unused,
+ struct audio_usecase *usecase __unused,
+ snd_device_t snd_device __unused)
+{
+ return false;
+}
+
bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev __unused,
struct audio_usecase *usecase __unused,
- snd_device_t snd_device)
+ snd_device_t snd_device __unused)
{
return false;
}
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 7b76df3..072a1e4 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1099,10 +1099,18 @@
snd_device_t snd_device,
char *device_name)
{
- device_name = platform_get_snd_device_name(snd_device);
+ strlcpy(device_name, platform_get_snd_device_name(snd_device),
+ DEVICE_NAME_MAX_SIZE);
return 0;
}
+bool platform_check_and_set_playback_backend_cfg(struct audio_device* adev __unused,
+ struct audio_usecase *usecase __unused,
+ snd_device_t snd_device __unused)
+{
+ return false;
+}
+
bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev __unused,
struct audio_usecase *usecase __unused)
{
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 5aaf93a..0f3bcf0 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2016 The Android Open Source Project
+ * Copyright (C) 2013-2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,8 +28,8 @@
#include "audio_extn.h"
#include <linux/msm_audio.h>
-#define MIXER_XML_DEFAULT_PATH "/system/etc/mixer_paths.xml"
-#define MIXER_XML_BASE_STRING "/system/etc/mixer_paths"
+#define MIXER_XML_DEFAULT_PATH "mixer_paths.xml"
+#define MIXER_XML_BASE_STRING "mixer_paths"
#define TOMTOM_8226_SND_CARD_NAME "msm8226-tomtom-snd-card"
#define TOMTOM_MIXER_FILE_SUFFIX "wcd9330"
@@ -120,7 +120,7 @@
bool speaker_lr_swap;
void *acdb_handle;
-#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996)
+#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
acdb_init_v2_cvd_t acdb_init;
#elif defined (PLATFORM_MSM8084)
acdb_init_v2_t acdb_init;
@@ -138,6 +138,7 @@
struct csd_data *csd;
char ec_ref_mixer_path[64];
+ codec_backend_cfg_t current_backend_cfg[MAX_CODEC_BACKENDS];
char *snd_card_name;
int max_vol_index;
int max_mic_count;
@@ -158,12 +159,17 @@
MULTIMEDIA2_PCM_DEVICE},
[USECASE_AUDIO_PLAYBACK_ULL] = {MULTIMEDIA3_PCM_DEVICE,
MULTIMEDIA3_PCM_DEVICE},
+ [USECASE_AUDIO_PLAYBACK_MMAP] = {MMAP_PLAYBACK_PCM_DEVICE,
+ MMAP_PLAYBACK_PCM_DEVICE},
[USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE,
AUDIO_RECORD_PCM_DEVICE},
[USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
LOWLATENCY_PCM_DEVICE},
+ [USECASE_AUDIO_RECORD_MMAP] = {MMAP_RECORD_PCM_DEVICE,
+ MMAP_RECORD_PCM_DEVICE},
+
[USECASE_VOICE_CALL] = {VOICE_CALL_PCM_DEVICE,
VOICE_CALL_PCM_DEVICE},
[USECASE_VOICE2_CALL] = {VOICE2_CALL_PCM_DEVICE, VOICE2_CALL_PCM_DEVICE},
@@ -221,10 +227,17 @@
[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones",
[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset",
+ [SND_DEVICE_OUT_VOICE_TTY_FULL_USB] = "voice-tty-full-usb",
+ [SND_DEVICE_OUT_VOICE_TTY_VCO_USB] = "voice-tty-vco-usb",
[SND_DEVICE_OUT_VOICE_TX] = "voice-tx",
+ [SND_DEVICE_OUT_USB_HEADSET] = "usb-headset",
+ [SND_DEVICE_OUT_USB_HEADPHONES] = "usb-headphones",
+ [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = "speaker-and-usb-headphones",
[SND_DEVICE_OUT_SPEAKER_PROTECTED] = "speaker-protected",
[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = "voice-speaker-protected",
[SND_DEVICE_OUT_VOICE_SPEAKER_HFP] = "voice-speaker-hfp",
+ [SND_DEVICE_OUT_SPEAKER_AND_BT_SCO] = "speaker-and-bt-sco",
+ [SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB] = "speaker-and-bt-sco-wb",
/* Capture sound devices */
[SND_DEVICE_IN_HANDSET_MIC] = "handset-mic",
@@ -266,6 +279,8 @@
[SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = "voice-tty-full-headset-mic",
[SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = "voice-tty-vco-handset-mic",
[SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = "voice-tty-hco-headset-mic",
+ [SND_DEVICE_IN_VOICE_TTY_FULL_USB_MIC] = "voice-tty-full-usb-mic",
+ [SND_DEVICE_IN_VOICE_TTY_HCO_USB_MIC] = "voice-tty-hco-usb-mic",
[SND_DEVICE_IN_VOICE_REC_MIC] = "voice-rec-mic",
[SND_DEVICE_IN_VOICE_REC_MIC_NS] = "voice-rec-mic",
@@ -273,6 +288,7 @@
[SND_DEVICE_IN_VOICE_REC_MIC_AEC_NS] = "voice-rec-mic",
[SND_DEVICE_IN_VOICE_REC_DMIC_STEREO] = "voice-rec-dmic-ef",
[SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence",
+ [SND_DEVICE_IN_USB_HEADSET_MIC] = "usb-headset-mic",
[SND_DEVICE_IN_VOICE_REC_HEADSET_MIC] = "headset-mic",
[SND_DEVICE_IN_UNPROCESSED_MIC] = "unprocessed-mic",
@@ -318,7 +334,12 @@
[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17,
[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
+ [SND_DEVICE_OUT_VOICE_TTY_FULL_USB] = 17,
+ [SND_DEVICE_OUT_VOICE_TTY_VCO_USB] = 17,
[SND_DEVICE_OUT_VOICE_TX] = 45,
+ [SND_DEVICE_OUT_USB_HEADSET] = 45,
+ [SND_DEVICE_OUT_USB_HEADPHONES] = 45,
+ [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 14,
[SND_DEVICE_OUT_SPEAKER_PROTECTED] = 124,
[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = 101,
[SND_DEVICE_OUT_VOICE_SPEAKER_HFP] = ACDB_ID_VOICE_SPEAKER,
@@ -362,6 +383,8 @@
[SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = 16,
[SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = 36,
[SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = 16,
+ [SND_DEVICE_IN_VOICE_TTY_FULL_USB_MIC] = 16,
+ [SND_DEVICE_IN_VOICE_TTY_HCO_USB_MIC] = 16,
[SND_DEVICE_IN_VOICE_REC_MIC] = ACDB_ID_VOICE_REC_MIC,
[SND_DEVICE_IN_VOICE_REC_MIC_NS] = 113,
@@ -378,7 +401,7 @@
[SND_DEVICE_IN_UNPROCESSED_QUAD_MIC] = 125,
[SND_DEVICE_IN_VOICE_RX] = 44,
-
+ [SND_DEVICE_IN_USB_HEADSET_MIC] = 44,
[SND_DEVICE_IN_THREE_MIC] = 46,
[SND_DEVICE_IN_QUAD_MIC] = 46,
[SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = 102,
@@ -388,6 +411,9 @@
[SND_DEVICE_IN_HANDSET_QMIC_AEC] = 125, /* override this for new target to 140 */
};
+// Platform specific backend bit width table
+static int backend_bit_width_table[SND_DEVICE_MAX] = {0};
+
struct name_to_index {
char name[100];
unsigned int index;
@@ -422,10 +448,17 @@
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES)},
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES)},
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)},
-
- /* in */
+ {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_SCO)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_USB)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_USB)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_USB_HEADSET)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_USB_HEADPHONES)},
+ {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET)},
{TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_PROTECTED)},
{TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED)},
+
+ /* in */
{TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC)},
{TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_NS)},
@@ -465,6 +498,9 @@
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC)},
+ {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_FULL_USB_MIC)},
+ {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_HCO_USB_MIC)},
+
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC_NS)},
@@ -473,6 +509,7 @@
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_STEREO)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE)},
{TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_HEADSET_MIC)},
+ {TO_NAME_INDEX(SND_DEVICE_IN_USB_HEADSET_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_UNPROCESSED_MIC)},
{TO_NAME_INDEX(SND_DEVICE_IN_UNPROCESSED_HEADSET_MIC)},
@@ -499,24 +536,32 @@
{TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD)},
{TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_TTS)},
{TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_ULL)},
+ {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_MMAP)},
{TO_NAME_INDEX(USECASE_AUDIO_RECORD)},
{TO_NAME_INDEX(USECASE_AUDIO_RECORD_LOW_LATENCY)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_MMAP)},
{TO_NAME_INDEX(USECASE_VOICE_CALL)},
{TO_NAME_INDEX(USECASE_VOICE2_CALL)},
{TO_NAME_INDEX(USECASE_VOLTE_CALL)},
{TO_NAME_INDEX(USECASE_QCHAT_CALL)},
{TO_NAME_INDEX(USECASE_VOWLAN_CALL)},
+ {TO_NAME_INDEX(USECASE_VOICEMMODE1_CALL)},
+ {TO_NAME_INDEX(USECASE_VOICEMMODE2_CALL)},
{TO_NAME_INDEX(USECASE_INCALL_REC_UPLINK)},
{TO_NAME_INDEX(USECASE_INCALL_REC_DOWNLINK)},
{TO_NAME_INDEX(USECASE_INCALL_REC_UPLINK_AND_DOWNLINK)},
{TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO)},
- {TO_NAME_INDEX(USECASE_VOICEMMODE1_CALL)},
- {TO_NAME_INDEX(USECASE_VOICEMMODE2_CALL)},
+ {TO_NAME_INDEX(USECASE_AUDIO_SPKR_CALIB_RX)},
+ {TO_NAME_INDEX(USECASE_AUDIO_SPKR_CALIB_TX)},
+ {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_AFE_PROXY)},
+ {TO_NAME_INDEX(USECASE_AUDIO_RECORD_AFE_PROXY)},
+ {TO_NAME_INDEX(USECASE_AUDIO_DSM_FEEDBACK)},
};
#define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
#define LOW_LATENCY_PLATFORM_DELAY (13*1000LL)
-#define ULL_PLATFORM_DELAY (7*1000LL)
+#define ULL_PLATFORM_DELAY (3*1000LL)
+#define MMAP_PLATFORM_DELAY (3*1000LL)
static pthread_once_t check_op_once_ctl = PTHREAD_ONCE_INIT;
static bool is_tmus = false;
@@ -890,6 +935,10 @@
operator_specific_device_table[dev] = NULL;
}
+ for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
+ backend_bit_width_table[dev] = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ }
+
// To overwrite these go to the audio_platform_info.xml file.
backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
@@ -902,6 +951,11 @@
backend_tag_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
backend_tag_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
+ backend_tag_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("usb-headset");
+ backend_tag_table[SND_DEVICE_OUT_USB_HEADPHONES] = strdup("usb-headphones");
+ backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] =
+ strdup("speaker-and-usb-headphones");
+ backend_tag_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic");
hw_interface_table[SND_DEVICE_OUT_HANDSET] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_SPEAKER] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_SPEAKER_REVERSE] = strdup("SLIMBUS_0_RX");
@@ -925,6 +979,9 @@
hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = strdup("SLIMBUS_0_RX");
+ hw_interface_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("USB_AUDIO_RX");
+ hw_interface_table[SND_DEVICE_OUT_USB_HEADPHONES] = strdup("USB_AUDIO_RX");
+ hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = strdup("SLIMBUS_0_RX-and-USB_AUDIO_RX");
hw_interface_table[SND_DEVICE_OUT_VOICE_TX] = strdup("AFE_PCM_RX");
hw_interface_table[SND_DEVICE_OUT_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
@@ -974,7 +1031,7 @@
return 0;
}
-#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996)
+#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
char *cvd_version = calloc(1, MAX_CVD_VERSION_STRING_SIZE);
if (!cvd_version)
ALOGE("failed to allocate cvd_version");
@@ -992,6 +1049,79 @@
return 0;
}
+static void
+platform_backend_config_init(struct platform_data *pdata)
+{
+ int i;
+
+ /* initialize backend config */
+ for (i = 0; i < MAX_CODEC_BACKENDS; i++) {
+ pdata->current_backend_cfg[i].sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ pdata->current_backend_cfg[i].bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ pdata->current_backend_cfg[i].channels = CODEC_BACKEND_DEFAULT_CHANNELS;
+
+ if (i > MAX_RX_CODEC_BACKENDS)
+ pdata->current_backend_cfg[i].channels = CODEC_BACKEND_DEFAULT_TX_CHANNELS;
+
+ pdata->current_backend_cfg[i].bitwidth_mixer_ctl = NULL;
+ pdata->current_backend_cfg[i].samplerate_mixer_ctl = NULL;
+ pdata->current_backend_cfg[i].channels_mixer_ctl = NULL;
+ }
+
+ pdata->current_backend_cfg[DEFAULT_CODEC_BACKEND].bitwidth_mixer_ctl =
+ strdup("SLIM_0_RX Format");
+ pdata->current_backend_cfg[DEFAULT_CODEC_BACKEND].samplerate_mixer_ctl =
+ strdup("SLIM_0_RX SampleRate");
+
+ pdata->current_backend_cfg[DEFAULT_CODEC_TX_BACKEND].bitwidth_mixer_ctl =
+ strdup("SLIM_0_TX Format");
+ pdata->current_backend_cfg[DEFAULT_CODEC_TX_BACKEND].samplerate_mixer_ctl =
+ strdup("SLIM_0_TX SampleRate");
+
+ pdata->current_backend_cfg[USB_AUDIO_TX_BACKEND].bitwidth_mixer_ctl =
+ strdup("USB_AUDIO_TX Format");
+ pdata->current_backend_cfg[USB_AUDIO_TX_BACKEND].samplerate_mixer_ctl =
+ strdup("USB_AUDIO_TX SampleRate");
+ pdata->current_backend_cfg[USB_AUDIO_TX_BACKEND].channels_mixer_ctl =
+ strdup("USB_AUDIO_TX Channels");
+
+ pdata->current_backend_cfg[HEADPHONE_BACKEND].bitwidth_mixer_ctl =
+ strdup("SLIM_6_RX Format");
+ pdata->current_backend_cfg[HEADPHONE_BACKEND].samplerate_mixer_ctl =
+ strdup("SLIM_6_RX SampleRate");
+
+ pdata->current_backend_cfg[USB_AUDIO_RX_BACKEND].bitwidth_mixer_ctl =
+ strdup("USB_AUDIO_RX Format");
+ pdata->current_backend_cfg[USB_AUDIO_RX_BACKEND].samplerate_mixer_ctl =
+ strdup("USB_AUDIO_RX SampleRate");
+
+ pdata->current_backend_cfg[USB_AUDIO_RX_BACKEND].channels = 1;
+ pdata->current_backend_cfg[USB_AUDIO_RX_BACKEND].channels_mixer_ctl =
+ strdup("USB_AUDIO_RX Channels");
+}
+
+// Treblized config files will be located in /odm/etc or /vendor/etc.
+static const char *kConfigLocationList[] =
+ {"/odm/etc", "/vendor/etc", "/system/etc"};
+static const int kConfigLocationListSize =
+ (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
+
+bool resolveConfigFile(char file_name[MIXER_PATH_MAX_LENGTH]) {
+ char full_config_path[MIXER_PATH_MAX_LENGTH];
+ for (int i = 0; i < kConfigLocationListSize; i++) {
+ snprintf(full_config_path,
+ MIXER_PATH_MAX_LENGTH,
+ "%s/%s",
+ kConfigLocationList[i],
+ file_name);
+ if (F_OK == access(full_config_path, 0)) {
+ strcpy(file_name, full_config_path);
+ return true;
+ }
+ }
+ return false;
+}
+
void *platform_init(struct audio_device *adev)
{
char value[PROPERTY_VALUE_MAX];
@@ -1075,15 +1205,15 @@
snprintf(mixer_xml_file, sizeof(mixer_xml_file), "%s_%s_%s.xml",
MIXER_XML_BASE_STRING, snd_split_handle->snd_card,
snd_split_handle->form_factor);
-
- if (F_OK != access(mixer_xml_file, 0)) {
+ if (!resolveConfigFile(mixer_xml_file)) {
memset(mixer_xml_file, 0, sizeof(mixer_xml_file));
snprintf(mixer_xml_file, sizeof(mixer_xml_file), "%s_%s.xml",
MIXER_XML_BASE_STRING, snd_split_handle->snd_card);
- if (F_OK != access(mixer_xml_file, 0)) {
+ if (!resolveConfigFile(mixer_xml_file)) {
memset(mixer_xml_file, 0, sizeof(mixer_xml_file));
strlcpy(mixer_xml_file, MIXER_XML_DEFAULT_PATH, MIXER_PATH_MAX_LENGTH);
+ resolveConfigFile(mixer_xml_file);
}
}
@@ -1091,14 +1221,15 @@
PLATFORM_INFO_XML_BASE_STRING, snd_split_handle->snd_card,
snd_split_handle->form_factor);
- if (F_OK != access(platform_info_file, 0)) {
+ if (!resolveConfigFile(platform_info_file)) {
memset(platform_info_file, 0, sizeof(platform_info_file));
snprintf(platform_info_file, sizeof(platform_info_file), "%s_%s.xml",
PLATFORM_INFO_XML_BASE_STRING, snd_split_handle->snd_card);
- if (F_OK != access(platform_info_file, 0)) {
+ if (!resolveConfigFile(platform_info_file)) {
memset(platform_info_file, 0, sizeof(platform_info_file));
strlcpy(platform_info_file, PLATFORM_INFO_XML_PATH, MIXER_PATH_MAX_LENGTH);
+ resolveConfigFile(platform_info_file);
}
}
}
@@ -1253,7 +1384,7 @@
ALOGV("%s: Could not find the symbol acdb_loader_send_gain_dep_cal from %s",
__func__, LIB_ACDB_LOADER);
-#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996)
+#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
acdb_init_v2_cvd_t acdb_init;
acdb_init = (acdb_init_v2_cvd_t)dlsym(my_data->acdb_handle,
"acdb_loader_init_v2");
@@ -1290,6 +1421,9 @@
platform_acdb_init(my_data);
}
+ /* init usb */
+ audio_extn_usb_init(adev);
+
audio_extn_spkr_prot_init(adev);
audio_extn_hwdep_cal_send(adev->snd_card, my_data->acdb_handle);
@@ -1297,6 +1431,8 @@
/* load csd client */
platform_csd_init(my_data);
+ platform_backend_config_init(my_data);
+
return my_data;
init_failed:
@@ -1347,7 +1483,11 @@
free(info_item);
}
+ mixer_close(my_data->adev->mixer);
free(platform);
+
+ /* deinit usb */
+ audio_extn_usb_deinit();
}
const char *platform_get_snd_device_name(snd_device_t snd_device)
@@ -1366,9 +1506,10 @@
{
struct platform_data *my_data = (struct platform_data *)platform;
- if (platform == NULL || device_name == NULL) {
+ if (platform == NULL) {
ALOGW("%s: something wrong, use legacy get_snd_device name", __func__);
- device_name = platform_get_snd_device_name(snd_device);
+ strlcpy(device_name, platform_get_snd_device_name(snd_device),
+ DEVICE_NAME_MAX_SIZE);
} else if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) {
if (operator_specific_device_table[snd_device] != NULL) {
strlcpy(device_name, get_operator_specific_device_mixer_path(snd_device),
@@ -1527,8 +1668,8 @@
return ret;
}
-int platform_get_default_app_type_v2(void *platform, usecase_type_t type __unused,
- int *app_type)
+int platform_get_default_app_type_v2(void *platform __unused, usecase_type_t type __unused,
+ int *app_type __unused)
{
ALOGE("%s: Not implemented", __func__);
return -ENOSYS;
@@ -1547,6 +1688,38 @@
return acdb_device_table[snd_device];
}
+static int platform_get_backend_index(snd_device_t snd_device)
+{
+ int32_t port = DEFAULT_CODEC_BACKEND;
+
+ if (snd_device >= SND_DEVICE_OUT_BEGIN && snd_device < SND_DEVICE_OUT_END) {
+ if (backend_tag_table[snd_device] != NULL) {
+ if (strncmp(backend_tag_table[snd_device], "headphones",
+ sizeof("headphones")) == 0)
+ port = HEADPHONE_BACKEND;
+ else if (strcmp(backend_tag_table[snd_device], "hdmi") == 0)
+ port = HDMI_RX_BACKEND;
+ else if ((strcmp(backend_tag_table[snd_device], "usb-headphones") == 0) ||
+ (strcmp(backend_tag_table[snd_device], "usb-headset") == 0))
+ port = USB_AUDIO_RX_BACKEND;
+ }
+ } else if (snd_device >= SND_DEVICE_IN_BEGIN && snd_device < SND_DEVICE_IN_END) {
+ port = DEFAULT_CODEC_TX_BACKEND;
+ 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);
+ }
+
+ ALOGV("%s:napb: backend port - %d device - %d ", __func__, port, snd_device);
+
+ return port;
+}
+
int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
{
struct platform_data *my_data = (struct platform_data *)platform;
@@ -1910,6 +2083,26 @@
new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
new_snd_devices[1] = SND_DEVICE_OUT_LINE;
ret = 0;
+ } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_SCO &&
+ !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER,
+ SND_DEVICE_OUT_BT_SCO)) {
+ *num_devices = 2;
+ new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+ new_snd_devices[1] = SND_DEVICE_OUT_BT_SCO;
+ ret = 0;
+ } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB &&
+ !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER,
+ SND_DEVICE_OUT_BT_SCO_WB)) {
+ *num_devices = 2;
+ new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+ new_snd_devices[1] = SND_DEVICE_OUT_BT_SCO_WB;
+ ret = 0;
+ } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET &&
+ !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_USB_HEADSET)) {
+ *num_devices = 2;
+ new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+ new_snd_devices[1] = SND_DEVICE_OUT_USB_HEADSET;
+ ret = 0;
}
return ret;
}
@@ -1948,6 +2141,14 @@
} else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
AUDIO_DEVICE_OUT_SPEAKER)) {
snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI;
+ } else if ((devices & AUDIO_DEVICE_OUT_ALL_SCO) &&
+ ((devices & ~AUDIO_DEVICE_OUT_ALL_SCO) == AUDIO_DEVICE_OUT_SPEAKER)) {
+ snd_device = adev->bt_wb_speech_enabled ?
+ SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB :
+ SND_DEVICE_OUT_SPEAKER_AND_BT_SCO;
+ } else if (devices == (AUDIO_DEVICE_OUT_USB_DEVICE |
+ AUDIO_DEVICE_OUT_SPEAKER)) {
+ snd_device = SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET;
} else {
ALOGE("%s: Invalid combo device(%#x)", __func__, devices);
goto exit;
@@ -1981,6 +2182,24 @@
else
snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES;
}
+ } else if (devices & AUDIO_DEVICE_OUT_USB_DEVICE) {
+ if (voice_is_in_call(adev)) {
+ switch (adev->voice.tty_mode) {
+ case TTY_MODE_FULL:
+ snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_USB;
+ break;
+ case TTY_MODE_VCO:
+ snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_USB;
+ break;
+ case TTY_MODE_HCO:
+ // since Hearing will be on handset\speaker, use existing device
+ snd_device = SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET;
+ break;
+ default:
+ ALOGE("%s: Invalid TTY mode (%#x)",
+ __func__, adev->voice.tty_mode);
+ }
+ }
} else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
if (adev->bt_wb_speech_enabled) {
snd_device = SND_DEVICE_OUT_BT_SCO_WB;
@@ -2028,7 +2247,12 @@
}
} else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
snd_device = SND_DEVICE_OUT_HDMI ;
- } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
+ } else if (devices & AUDIO_DEVICE_OUT_USB_DEVICE) {
+ if (audio_extn_usb_is_capture_supported())
+ snd_device = SND_DEVICE_OUT_USB_HEADSET;
+ else
+ snd_device = SND_DEVICE_OUT_USB_HEADPHONES;
+ }else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
/*HAC support for voice-ish audio (eg visual voicemail)*/
if(adev->voice.hac)
snd_device = SND_DEVICE_OUT_VOICE_HAC_HANDSET;
@@ -2067,17 +2291,33 @@
out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET ||
out_device & AUDIO_DEVICE_OUT_LINE) {
switch (adev->voice.tty_mode) {
- case TTY_MODE_FULL:
- snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC;
- break;
- case TTY_MODE_VCO:
- snd_device = SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC;
- break;
- case TTY_MODE_HCO:
- snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC;
- break;
- default:
- ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice.tty_mode);
+ case TTY_MODE_FULL:
+ snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC;
+ break;
+ case TTY_MODE_VCO:
+ snd_device = SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC;
+ break;
+ case TTY_MODE_HCO:
+ snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC;
+ break;
+ default:
+ ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice.tty_mode);
+ }
+ goto exit;
+ } else if (out_device & AUDIO_DEVICE_OUT_USB_DEVICE) {
+ switch (adev->voice.tty_mode) {
+ case TTY_MODE_FULL:
+ snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_USB_MIC;
+ break;
+ case TTY_MODE_VCO:
+ // since voice will be captured from handset mic, use existing device
+ snd_device = SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC;
+ break;
+ case TTY_MODE_HCO:
+ snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_USB_MIC;
+ break;
+ default:
+ ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice.tty_mode);
}
goto exit;
}
@@ -2161,10 +2401,10 @@
} else if ((channel_mask == AUDIO_CHANNEL_IN_FRONT_BACK) &&
(my_data->source_mic_type & SOURCE_DUAL_MIC)) {
snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_STEREO;
- } else if (((int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_3) &&
+ } else if ((channel_mask == AUDIO_CHANNEL_INDEX_MASK_3) &&
(my_data->source_mic_type & SOURCE_THREE_MIC)) {
snd_device = SND_DEVICE_IN_THREE_MIC;
- } else if (((int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_4) &&
+ } else if ((channel_mask == AUDIO_CHANNEL_INDEX_MASK_4) &&
(my_data->source_mic_type & SOURCE_QUAD_MIC)) {
snd_device = SND_DEVICE_IN_QUAD_MIC;
}
@@ -2191,10 +2431,10 @@
(channel_mask == AUDIO_CHANNEL_IN_STEREO)) &&
(my_data->source_mic_type & SOURCE_DUAL_MIC)) {
snd_device = SND_DEVICE_IN_UNPROCESSED_STEREO_MIC;
- } else if (((int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_3) &&
+ } else if ((channel_mask == AUDIO_CHANNEL_INDEX_MASK_3) &&
(my_data->source_mic_type & SOURCE_THREE_MIC)) {
snd_device = SND_DEVICE_IN_UNPROCESSED_THREE_MIC;
- } else if (((int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_4) &&
+ } else if ((channel_mask == AUDIO_CHANNEL_INDEX_MASK_4) &&
(my_data->source_mic_type & SOURCE_QUAD_MIC)) {
snd_device = SND_DEVICE_IN_UNPROCESSED_QUAD_MIC;
} else {
@@ -2282,10 +2522,10 @@
!(in_device & AUDIO_DEVICE_IN_COMMUNICATION)) {
if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
if ((my_data->source_mic_type & SOURCE_QUAD_MIC) &&
- (int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_4) {
+ channel_mask == AUDIO_CHANNEL_INDEX_MASK_4) {
snd_device = SND_DEVICE_IN_QUAD_MIC;
} else if ((my_data->source_mic_type & SOURCE_THREE_MIC) &&
- (int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_3) {
+ channel_mask == AUDIO_CHANNEL_INDEX_MASK_3) {
snd_device = SND_DEVICE_IN_THREE_MIC;
} else if ((my_data->source_mic_type & SOURCE_DUAL_MIC) &&
channel_count == 2) {
@@ -2328,6 +2568,8 @@
}
} else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) {
snd_device = SND_DEVICE_IN_HDMI_MIC;
+ } else if (in_device & AUDIO_DEVICE_IN_USB_DEVICE ) {
+ snd_device = SND_DEVICE_IN_USB_HEADSET_MIC;
} else {
ALOGE("%s: Unknown input device(s) %#x", __func__, in_device);
ALOGW("%s: Using default handset-mic", __func__);
@@ -2368,6 +2610,11 @@
}
} else if (out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
snd_device = SND_DEVICE_IN_HDMI_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_USB_DEVICE) {
+ if (audio_extn_usb_is_capture_supported())
+ snd_device = SND_DEVICE_IN_USB_HEADSET_MIC;
+ else
+ snd_device = SND_DEVICE_IN_SPEAKER_MIC;
} else {
ALOGE("%s: Unknown output device(s) %#x", __func__, out_device);
ALOGW("%s: Using default handset-mic", __func__);
@@ -2621,31 +2868,13 @@
return LOW_LATENCY_PLATFORM_DELAY;
case USECASE_AUDIO_PLAYBACK_ULL:
return ULL_PLATFORM_DELAY;
+ case USECASE_AUDIO_PLAYBACK_MMAP:
+ return MMAP_PLATFORM_DELAY;
default:
return 0;
}
}
-bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev,
- struct audio_usecase *usecase, snd_device_t snd_device)
-{
- enum pcm_format in_pcm_format = PCM_FORMAT_S16_LE;
-
- if (adev && adev->active_input)
- in_pcm_format = adev->active_input->config.format;
-
- // allow 24 bit recording only if voice call is not active
- if (!voice_is_in_call(adev) &&
- adev->mode != AUDIO_MODE_IN_COMMUNICATION &&
- in_pcm_format == PCM_FORMAT_S24_LE) {
- audio_route_apply_and_update_path(adev->audio_route, "set-capture-format-24le");
- } else {
- audio_route_apply_and_update_path(adev->audio_route, "set-capture-format-default");
- }
-
- return true;
-}
-
int platform_set_snd_device_backend(snd_device_t device, const char *backend_tag,
const char * hw_interface)
{
@@ -2868,3 +3097,472 @@
}
return 0;
}
+
+/*
+ * configures afe with bit width and Sample Rate
+ */
+static int platform_set_backend_cfg(const struct audio_device* adev,
+ snd_device_t snd_device,
+ const struct audio_backend_cfg *backend_cfg)
+{
+
+ int ret = 0;
+ const int backend_idx = platform_get_backend_index(snd_device);
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+ const unsigned int bit_width = backend_cfg->bit_width;
+ const unsigned int sample_rate = backend_cfg->sample_rate;
+ const unsigned int channels = backend_cfg->channels;
+ const audio_format_t format = backend_cfg->format;
+ const bool passthrough_enabled = backend_cfg->passthrough_enabled;
+
+
+ ALOGV("%s:becf: afe: bitwidth %d, samplerate %d channels %d"
+ ", backend_idx %d device (%s)", __func__, bit_width,
+ sample_rate, channels, backend_idx,
+ platform_get_snd_device_name(snd_device));
+
+ 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,
+ my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl);
+ if (!ctl) {
+ ALOGE("%s:becf: afe: Could not get ctl for mixer command - %s",
+ __func__,
+ my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl);
+ return -EINVAL;
+ }
+
+ if (bit_width == 24) {
+ if (format == AUDIO_FORMAT_PCM_24_BIT_PACKED)
+ ret = mixer_ctl_set_enum_by_string(ctl, "S24_3LE");
+ else
+ ret = mixer_ctl_set_enum_by_string(ctl, "S24_LE");
+ } else if (bit_width == 32) {
+ ret = mixer_ctl_set_enum_by_string(ctl, "S32_LE");
+ } else {
+ ret = mixer_ctl_set_enum_by_string(ctl, "S16_LE");
+ }
+ if ( ret < 0) {
+ ALOGE("%s:becf: afe: fail for %s mixer set to %d bit for %x format", __func__,
+ my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl, bit_width, format);
+ } else {
+ my_data->current_backend_cfg[backend_idx].bit_width = bit_width;
+ ALOGD("%s:becf: afe: %s mixer set to %d bit for %x format", __func__,
+ my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl, bit_width, format);
+ }
+ /* set the ret as 0 and not pass back to upper layer */
+ ret = 0;
+ }
+
+ if (passthrough_enabled || ((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;
+
+ switch (sample_rate) {
+ case 32000:
+ if (passthrough_enabled) {
+ rate_str = "KHZ_32";
+ break;
+ }
+ case 8000:
+ case 11025:
+ case 16000:
+ case 22050:
+ case 48000:
+ rate_str = "KHZ_48";
+ break;
+ case 44100:
+ rate_str = "KHZ_44P1";
+ break;
+ case 64000:
+ case 96000:
+ rate_str = "KHZ_96";
+ break;
+ case 88200:
+ rate_str = "KHZ_88P2";
+ break;
+ case 176400:
+ rate_str = "KHZ_176P4";
+ break;
+ case 192000:
+ rate_str = "KHZ_192";
+ break;
+ case 352800:
+ rate_str = "KHZ_352P8";
+ break;
+ case 384000:
+ rate_str = "KHZ_384";
+ break;
+ case 144000:
+ if (passthrough_enabled) {
+ rate_str = "KHZ_144";
+ break;
+ }
+ default:
+ rate_str = "KHZ_48";
+ break;
+ }
+
+ ctl = mixer_get_ctl_by_name(adev->mixer,
+ my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl);
+ if(!ctl) {
+ ALOGE("%s:becf: afe: Could not get ctl for mixer command - %s",
+ __func__,
+ my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl);
+ return -EINVAL;
+ }
+
+ ALOGD("%s:becf: afe: %s set to %s", __func__,
+ my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl, rate_str);
+ mixer_ctl_set_enum_by_string(ctl, rate_str);
+ my_data->current_backend_cfg[backend_idx].sample_rate = sample_rate;
+ }
+ if ((my_data->current_backend_cfg[backend_idx].channels_mixer_ctl) &&
+ (channels != my_data->current_backend_cfg[backend_idx].channels)) {
+ struct mixer_ctl *ctl = NULL;
+ char *channel_cnt_str = NULL;
+
+ switch (channels) {
+ case 8:
+ channel_cnt_str = "Eight"; break;
+ case 7:
+ channel_cnt_str = "Seven"; break;
+ case 6:
+ channel_cnt_str = "Six"; break;
+ case 5:
+ channel_cnt_str = "Five"; break;
+ case 4:
+ channel_cnt_str = "Four"; break;
+ case 3:
+ channel_cnt_str = "Three"; break;
+ case 1:
+ channel_cnt_str = "One"; break;
+ case 2:
+ default:
+ channel_cnt_str = "Two"; break;
+ }
+
+ ctl = mixer_get_ctl_by_name(adev->mixer,
+ my_data->current_backend_cfg[backend_idx].channels_mixer_ctl);
+ if (!ctl) {
+ ALOGE("%s:becf: afe: Could not get ctl for mixer command - %s",
+ __func__,
+ my_data->current_backend_cfg[backend_idx].channels_mixer_ctl);
+ return -EINVAL;
+ }
+ mixer_ctl_set_enum_by_string(ctl, channel_cnt_str);
+ my_data->current_backend_cfg[backend_idx].channels = channels;
+
+ // skip EDID configuration for HDMI backend
+
+ ALOGD("%s:becf: afe: %s set to %s", __func__,
+ my_data->current_backend_cfg[backend_idx].channels_mixer_ctl,
+ channel_cnt_str);
+ }
+
+ // skip set ext_display format mixer control
+ return ret;
+}
+
+static int platform_get_snd_device_bit_width(snd_device_t snd_device)
+{
+ if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+ ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
+ return CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ }
+
+ return backend_bit_width_table[snd_device];
+}
+
+/*
+ * return backend_idx on which voice call is active
+ */
+static int platform_get_voice_call_backend(struct audio_device* adev)
+{
+ struct audio_usecase *uc = NULL;
+ struct listnode *node;
+ snd_device_t out_snd_device = SND_DEVICE_NONE;
+
+ int backend_idx = -1;
+
+ if (voice_is_in_call(adev) || adev->mode == AUDIO_MODE_IN_COMMUNICATION) {
+ list_for_each(node, &adev->usecase_list) {
+ uc = node_to_item(node, struct audio_usecase, list);
+ if (uc && uc->type == VOICE_CALL && uc->stream.out) {
+ out_snd_device = platform_get_output_snd_device(adev->platform,
+ uc->stream.out->devices);
+ backend_idx = platform_get_backend_index(out_snd_device);
+ break;
+ }
+ }
+ }
+ return backend_idx;
+}
+
+/*
+ * goes through all the current usecases and picks the highest
+ * bitwidth & samplerate
+ */
+static bool platform_check_capture_backend_cfg(struct audio_device* adev,
+ int backend_idx,
+ struct audio_backend_cfg *backend_cfg)
+{
+ bool backend_change = false;
+ unsigned int bit_width;
+ unsigned int sample_rate;
+ unsigned int channels;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+ bit_width = backend_cfg->bit_width;
+ sample_rate = backend_cfg->sample_rate;
+ channels = backend_cfg->channels;
+
+ ALOGV("%s:txbecf: afe: Codec selected backend: %d current bit width: %d and "
+ "sample rate: %d, channels %d",__func__,backend_idx, bit_width,
+ sample_rate, channels);
+
+ // For voice calls use default configuration i.e. 16b/48K, only applicable to
+ // default backend
+ // force routing is not required here, caller will do it anyway
+ if (voice_is_in_call(adev) || adev->mode == AUDIO_MODE_IN_COMMUNICATION) {
+ ALOGW("%s:txbecf: afe: Use default bw and sr for voice/voip calls and "
+ "for unprocessed/camera source", __func__);
+ bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ }
+
+ if (backend_idx == USB_AUDIO_TX_BACKEND) {
+ audio_extn_usb_is_config_supported(&bit_width, &sample_rate, &channels, false);
+ ALOGV("%s:txbecf: afe: USB BE configured as bit_width(%d)sample_rate(%d)channels(%d)",
+ __func__, bit_width, sample_rate, channels);
+ }
+
+ ALOGV("%s:txbecf: afe: Codec selected backend: %d updated bit width: %d and "
+ "sample rate: %d", __func__, backend_idx, bit_width, sample_rate);
+
+ // Force routing if the expected bitwdith or samplerate
+ // is not same as current backend comfiguration
+ if ((bit_width != my_data->current_backend_cfg[backend_idx].bit_width) ||
+ (sample_rate != my_data->current_backend_cfg[backend_idx].sample_rate) ||
+ (channels != my_data->current_backend_cfg[backend_idx].channels)) {
+ backend_cfg->bit_width = bit_width;
+ backend_cfg->sample_rate= sample_rate;
+ backend_cfg->channels = channels;
+ backend_change = true;
+ ALOGI("%s:txbecf: afe: Codec backend needs to be updated. new bit width: %d "
+ "new sample rate: %d new channel: %d",
+ __func__, backend_cfg->bit_width,
+ backend_cfg->sample_rate, backend_cfg->channels);
+ }
+
+ return backend_change;
+}
+
+static void platform_pick_playback_cfg_for_uc(struct audio_device *adev,
+ struct audio_usecase *usecase,
+ snd_device_t snd_device,
+ unsigned int *bit_width,
+ unsigned int *sample_rate,
+ unsigned int *channels)
+{
+ int i =0;
+ struct listnode *node;
+ list_for_each(node, &adev->usecase_list) {
+ struct audio_usecase *uc;
+ uc = node_to_item(node, struct audio_usecase, list);
+ struct stream_out *out = (struct stream_out*) uc->stream.out;
+ if (uc->type == PCM_PLAYBACK && out && usecase != uc) {
+ unsigned int out_channels = audio_channel_count_from_out_mask(out->channel_mask);
+ ALOGV("%s:napb: (%d) - (%s)id (%d) sr %d bw "
+ "(%d) ch (%d) device %s", __func__, i++, use_case_table[uc->id],
+ uc->id, out->sample_rate,
+ pcm_format_to_bits(out->config.format), out_channels,
+ platform_get_snd_device_name(uc->out_snd_device));
+
+ if (platform_check_backends_match(snd_device, uc->out_snd_device)) {
+ if (*bit_width < pcm_format_to_bits(out->config.format))
+ *bit_width = pcm_format_to_bits(out->config.format);
+ if (*sample_rate < out->sample_rate)
+ *sample_rate = out->sample_rate;
+ if (out->sample_rate < OUTPUT_SAMPLING_RATE_44100)
+ *sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ if (*channels < out_channels)
+ *channels = out_channels;
+ }
+ }
+ }
+ return;
+}
+
+static bool platform_check_playback_backend_cfg(struct audio_device* adev,
+ struct audio_usecase* usecase,
+ snd_device_t snd_device,
+ struct audio_backend_cfg *backend_cfg)
+{
+ bool backend_change = false;
+ unsigned int bit_width;
+ unsigned int sample_rate;
+ unsigned int channels;
+ bool passthrough_enabled = false;
+ int backend_idx = DEFAULT_CODEC_BACKEND;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+ bool channels_updated = false;
+
+ if (snd_device == SND_DEVICE_OUT_BT_SCO ||
+ snd_device == SND_DEVICE_OUT_BT_SCO_WB) {
+ backend_change = false;
+ return backend_change;
+ }
+
+ backend_idx = platform_get_backend_index(snd_device);
+ bit_width = backend_cfg->bit_width;
+ sample_rate = backend_cfg->sample_rate;
+ channels = backend_cfg->channels;
+
+ ALOGV("%s:becf: afe: bitwidth %d, samplerate %d channels %d"
+ ", backend_idx %d usecase = %d device (%s)", __func__, bit_width,
+ sample_rate, channels, backend_idx, usecase->id,
+ platform_get_snd_device_name(snd_device));
+
+ if (backend_idx == platform_get_voice_call_backend(adev)) {
+ ALOGW("%s:becf: afe:Use default bw and sr for voice/voip calls ",
+ __func__);
+ bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ channels = CODEC_BACKEND_DEFAULT_CHANNELS;
+ } else {
+ /*
+ * The backend should be configured at highest bit width and/or
+ * sample rate amongst all playback usecases.
+ * If the selected sample rate and/or bit width differ with
+ * current backend sample rate and/or bit width, then, we set the
+ * backend re-configuration flag.
+ *
+ * Exception: 16 bit playbacks is allowed through 16 bit/48/44.1 khz backend only
+ */
+ platform_pick_playback_cfg_for_uc(adev, usecase, snd_device,
+ &bit_width,
+ &sample_rate,
+ &channels);
+ }
+
+ switch (backend_idx) {
+ case USB_AUDIO_RX_BACKEND:
+ audio_extn_usb_is_config_supported(&bit_width,
+ &sample_rate, &channels, true);
+ ALOGV("%s: USB BE configured as bit_width(%d)sample_rate(%d)channels(%d)",
+ __func__, bit_width, sample_rate, channels);
+ break;
+ case DEFAULT_CODEC_BACKEND:
+ case HEADPHONE_BACKEND:
+ default:
+ bit_width = platform_get_snd_device_bit_width(snd_device);
+ sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ channels = CODEC_BACKEND_DEFAULT_CHANNELS;
+ break;
+ }
+
+ if (channels != my_data->current_backend_cfg[backend_idx].channels) {
+ channels_updated = true;
+ }
+
+ ALOGV("%s:becf: afe: Codec selected backend: %d updated bit width: %d and"
+ "sample rate: %d",
+ __func__, backend_idx , bit_width, sample_rate);
+
+ // Force routing if the expected bitwdith or samplerate
+ // is not same as current backend comfiguration
+ if ((bit_width != my_data->current_backend_cfg[backend_idx].bit_width) ||
+ (sample_rate != my_data->current_backend_cfg[backend_idx].sample_rate) ||
+ passthrough_enabled || channels_updated) {
+ backend_cfg->bit_width = bit_width;
+ backend_cfg->sample_rate = sample_rate;
+ backend_cfg->channels = channels;
+ backend_cfg->passthrough_enabled = passthrough_enabled;
+ backend_change = true;
+ ALOGV("%s:becf: afe: Codec backend needs to be updated. new bit width: %d"
+ "new sample rate: %d new channels: %d",
+ __func__, backend_cfg->bit_width, backend_cfg->sample_rate, backend_cfg->channels);
+ }
+
+ return backend_change;
+}
+
+bool platform_check_and_set_playback_backend_cfg(struct audio_device* adev,
+ struct audio_usecase *usecase, snd_device_t snd_device)
+{
+ int backend_idx = DEFAULT_CODEC_BACKEND;
+ int new_snd_devices[SND_DEVICE_OUT_END];
+ int i, num_devices = 1;
+ bool ret = false;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+ struct audio_backend_cfg backend_cfg;
+
+ backend_idx = platform_get_backend_index(snd_device);
+
+ backend_cfg.bit_width = pcm_format_to_bits(usecase->stream.out->config.format);
+ backend_cfg.sample_rate = usecase->stream.out->sample_rate;
+ backend_cfg.format = usecase->stream.out->format;
+ backend_cfg.channels = audio_channel_count_from_out_mask(usecase->stream.out->channel_mask);
+ /*this is populated by check_codec_backend_cfg hence set default value to false*/
+ backend_cfg.passthrough_enabled = false;
+
+ ALOGV("%s:becf: afe: bitwidth %d, samplerate %d channels %d"
+ ", backend_idx %d usecase = %d device (%s)", __func__, backend_cfg.bit_width,
+ backend_cfg.sample_rate, backend_cfg.channels, backend_idx, usecase->id,
+ platform_get_snd_device_name(snd_device));
+
+ if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices) < 0)
+ new_snd_devices[0] = snd_device;
+
+ for (i = 0; i < num_devices; i++) {
+ ALOGV("%s: new_snd_devices[%d] is %d", __func__, i, new_snd_devices[i]);
+ if ((platform_check_playback_backend_cfg(adev, usecase, new_snd_devices[i],
+ &backend_cfg))) {
+ platform_set_backend_cfg(adev, new_snd_devices[i],
+ &backend_cfg);
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev,
+ struct audio_usecase *usecase, snd_device_t snd_device)
+{
+ int backend_idx = platform_get_backend_index(snd_device);
+ int ret = 0;
+ struct audio_backend_cfg backend_cfg;
+ memset(&backend_cfg, 0, sizeof(struct audio_backend_cfg));
+
+ if (usecase->type == PCM_CAPTURE) {
+ backend_cfg.format = usecase->stream.in->format;
+ backend_cfg.channels = audio_channel_count_from_in_mask(usecase->stream.in->channel_mask);
+ } else {
+ backend_cfg.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+ backend_cfg.sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+ backend_cfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ backend_cfg.channels = 1;
+ }
+
+ ALOGV("%s:txbecf: afe: bitwidth %d, samplerate %d, channel %d"
+ ", backend_idx %d usecase = %d device (%s)", __func__,
+ backend_cfg.bit_width,
+ backend_cfg.sample_rate,
+ backend_cfg.channels,
+ backend_idx, usecase->id,
+ platform_get_snd_device_name(snd_device));
+
+ if (platform_check_capture_backend_cfg(adev, backend_idx, &backend_cfg)) {
+ ret = platform_set_backend_cfg(adev, snd_device,
+ &backend_cfg);
+ if(!ret)
+ return true;
+ }
+
+ return false;
+}
+
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index aa4c08d..b411e9d 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2016 The Android Open Source Project
+ * Copyright (C) 2013-2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,6 +41,15 @@
AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | \
AUDIO_DEVICE_OUT_LINE)
+/*
+ * Below are the input devices for which back end is same, SLIMBUS_0_TX.
+ * All these devices are handled by the internal HW codec. We can
+ * enable any one of these devices at any time
+ */
+#define AUDIO_DEVICE_IN_ALL_CODEC_BACKEND \
+ (AUDIO_DEVICE_IN_BUILTIN_MIC | AUDIO_DEVICE_IN_BACK_MIC | \
+ AUDIO_DEVICE_IN_WIRED_HEADSET | AUDIO_DEVICE_IN_VOICE_CALL) & ~AUDIO_DEVICE_BIT_IN
+
/* Sound devices specific to the platform
* The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound
* devices to enable corresponding mixer paths
@@ -73,11 +82,18 @@
SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
+ SND_DEVICE_OUT_VOICE_TTY_FULL_USB,
+ SND_DEVICE_OUT_VOICE_TTY_VCO_USB,
SND_DEVICE_OUT_VOICE_HAC_HANDSET,
SND_DEVICE_OUT_VOICE_TX,
SND_DEVICE_OUT_SPEAKER_PROTECTED,
SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED,
SND_DEVICE_OUT_VOICE_SPEAKER_HFP,
+ SND_DEVICE_OUT_SPEAKER_AND_BT_SCO,
+ SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB,
+ SND_DEVICE_OUT_USB_HEADSET,
+ SND_DEVICE_OUT_USB_HEADPHONES,
+ SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET,
SND_DEVICE_OUT_END,
/*
@@ -125,6 +141,8 @@
SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC,
SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC,
SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC,
+ SND_DEVICE_IN_VOICE_TTY_FULL_USB_MIC,
+ SND_DEVICE_IN_VOICE_TTY_HCO_USB_MIC,
SND_DEVICE_IN_VOICE_REC_MIC,
SND_DEVICE_IN_VOICE_REC_MIC_NS,
@@ -142,6 +160,7 @@
SND_DEVICE_IN_VOICE_RX,
+ SND_DEVICE_IN_USB_HEADSET_MIC,
SND_DEVICE_IN_THREE_MIC,
SND_DEVICE_IN_QUAD_MIC,
SND_DEVICE_IN_CAPTURE_VI_FEEDBACK,
@@ -155,6 +174,24 @@
SND_DEVICE_MAX = SND_DEVICE_IN_END,
};
+#define DEFAULT_OUTPUT_SAMPLING_RATE 48000
+#define OUTPUT_SAMPLING_RATE_44100 44100
+enum {
+ DEFAULT_CODEC_BACKEND,
+ SLIMBUS_0_RX = DEFAULT_CODEC_BACKEND,
+ HEADPHONE_BACKEND,
+ SLIMBUS_6_RX = HEADPHONE_BACKEND,
+ HDMI_RX_BACKEND,
+ USB_AUDIO_RX_BACKEND,
+ MAX_RX_CODEC_BACKENDS = USB_AUDIO_RX_BACKEND,
+ /* TX BE follows RX BE */
+ SLIMBUS_0_TX,
+ DEFAULT_CODEC_TX_BACKEND = SLIMBUS_0_TX,
+ USB_AUDIO_TX_BACKEND,
+ BT_SCO_TX_BACKEND,
+ MAX_CODEC_BACKENDS
+};
+
#define DEVICE_NAME_MAX_SIZE 128
#define HW_INFO_ARRAY_MAX_SIZE 32
@@ -215,6 +252,9 @@
#define MULTIMEDIA3_PCM_DEVICE 4
+#define MMAP_PLAYBACK_PCM_DEVICE 18
+#define MMAP_RECORD_PCM_DEVICE 18
+
#define QUAT_MI2S_PCM_DEVICE 44
#define PLAYBACK_OFFLOAD_DEVICE 9
#define LOWLATENCY_PCM_DEVICE 15
@@ -313,6 +353,23 @@
get_sample_rate_t get_sample_rate;
};
-#define PLATFORM_INFO_XML_PATH "/system/etc/audio_platform_info.xml"
-#define PLATFORM_INFO_XML_BASE_STRING "/system/etc/audio_platform_info"
+struct audio_backend_cfg {
+ unsigned int sample_rate;
+ unsigned int channels;
+ unsigned int bit_width;
+ bool passthrough_enabled;
+ audio_format_t format;
+};
+
+typedef struct codec_backend_cfg {
+ uint32_t sample_rate;
+ uint32_t bit_width;
+ uint32_t channels;
+ char *bitwidth_mixer_ctl;
+ char *samplerate_mixer_ctl;
+ char *channels_mixer_ctl;
+} codec_backend_cfg_t;
+
+#define PLATFORM_INFO_XML_PATH "audio_platform_info.xml"
+#define PLATFORM_INFO_XML_BASE_STRING "audio_platform_info"
#endif // QCOM_AUDIO_PLATFORM_H
diff --git a/hal/platform_api.h b/hal/platform_api.h
index ebbe9d2..59ad4b1 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -20,6 +20,12 @@
#include "voice.h"
#define MAX_VOLUME_CAL_STEPS 15
#define CODEC_BACKEND_DEFAULT_SAMPLE_RATE 48000
+#define CODEC_BACKEND_DEFAULT_BIT_WIDTH 16
+#define CODEC_BACKEND_DEFAULT_CHANNELS 2
+#define CODEC_BACKEND_DEFAULT_TX_CHANNELS 1
+#define SAMPLE_RATE_8000 8000
+#define SAMPLE_RATE_11025 11025
+#define sample_rate_multiple(sr, base) ((sr % base)== 0?true:false)
struct amp_db_and_gain_table {
float amp;
@@ -28,6 +34,7 @@
};
enum card_status_t;
+struct audio_usecase;
enum usecase_type_t;
void *platform_init(struct audio_device *adev);
@@ -111,6 +118,9 @@
int platform_set_parameters(void *platform, struct str_parms *parms);
+bool platform_check_and_set_playback_backend_cfg(struct audio_device* adev,
+ struct audio_usecase *usecase, snd_device_t snd_device);
+
bool platform_check_and_set_capture_backend_cfg(struct audio_device* adev,
struct audio_usecase *usecase, snd_device_t snd_device);
diff --git a/legacy/libalsa-intf/alsa_pcm.c b/legacy/libalsa-intf/alsa_pcm.c
index a814ae8..84d6549 100644
--- a/legacy/libalsa-intf/alsa_pcm.c
+++ b/legacy/libalsa-intf/alsa_pcm.c
@@ -46,10 +46,6 @@
#include "alsa_audio.h"
-#define __force
-#define __bitwise
-#define __user
-
#define DEBUG 1
enum format_alias {
diff --git a/post_proc/Android.mk b/post_proc/Android.mk
index 83f6a49..eb59ddb 100644
--- a/post_proc/Android.mk
+++ b/post_proc/Android.mk
@@ -20,6 +20,8 @@
libtinyalsa
LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_OWNER := qcom
+LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libqcompostprocbundle
@@ -37,7 +39,7 @@
include $(CLEAR_VARS)
-LOCAL_CFLAGS := -DLIB_AUDIO_HAL="/system/lib/hw/audio.primary."$(TARGET_BOARD_PLATFORM)".so"
+LOCAL_CFLAGS := -DLIB_AUDIO_HAL="audio.primary."$(TARGET_BOARD_PLATFORM)".so"
LOCAL_SRC_FILES:= \
volume_listener.c
@@ -51,6 +53,8 @@
LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libvolumelistener
+LOCAL_MODULE_OWNER := qcom
+LOCAL_PROPRIETARY_MODULE := true
LOCAL_C_INCLUDES := \
hardware/qcom/audio/hal \
diff --git a/post_proc/bass_boost.c b/post_proc/bass_boost.c
index f303886..a5c6a4a 100644
--- a/post_proc/bass_boost.c
+++ b/post_proc/bass_boost.c
@@ -31,7 +31,8 @@
{0x0634f220, 0xddd4, 0x11db, 0xa0fc, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }},
{0x2c4a8c24, 0x1581, 0x487f, 0x94f6, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
EFFECT_CONTROL_API_VERSION,
- (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_HW_ACC_TUNNEL),
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_HW_ACC_TUNNEL |
+ EFFECT_FLAG_VOLUME_CTRL),
0, /* TODO */
1,
"MSM offload bassboost",
diff --git a/post_proc/bundle.c b/post_proc/bundle.c
index df327ab..f83e231 100644
--- a/post_proc/bundle.c
+++ b/post_proc/bundle.c
@@ -244,7 +244,7 @@
__attribute__ ((visibility ("default")))
int offload_effects_bundle_hal_stop_output(audio_io_handle_t output, int pcm_id)
{
- int ret;
+ int ret = 0;
struct listnode *node;
struct listnode *fx_node;
output_context_t *out_ctxt;
@@ -668,10 +668,19 @@
if (context->ops.set_device)
context->ops.set_device(context, device);
} break;
- case EFFECT_CMD_SET_VOLUME:
+ case EFFECT_CMD_SET_VOLUME: {
+ // if pReplyData is NULL, VOL_CTRL is delegated to another effect
+ if (pReplyData == NULL) {
+ break;
+ }
+ if (pCmdData == NULL || cmdSize != 2 * sizeof(uint32_t) ||
+ replySize == NULL || *replySize < 2*sizeof(int32_t)) {
+ return -EINVAL;
+ }
+ memcpy(pReplyData, pCmdData, sizeof(int32_t)*2);
+ } break;
case EFFECT_CMD_SET_AUDIO_MODE:
break;
-
case EFFECT_CMD_OFFLOAD: {
output_context_t *out_ctxt;
@@ -703,8 +712,6 @@
add_effect_to_output(out_ctxt, context);
} break;
-
-
default:
if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command)
status = context->ops.command(context, cmdCode, cmdSize,
@@ -750,11 +757,11 @@
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
- tag : AUDIO_EFFECT_LIBRARY_TAG,
- version : EFFECT_LIBRARY_API_VERSION,
- name : "Offload Effects Bundle Library",
- implementor : "The Android Open Source Project",
- create_effect : effect_lib_create,
- release_effect : effect_lib_release,
- get_descriptor : effect_lib_get_descriptor,
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "Offload Effects Bundle Library",
+ .implementor = "The Android Open Source Project",
+ .create_effect = effect_lib_create,
+ .release_effect = effect_lib_release,
+ .get_descriptor = effect_lib_get_descriptor,
};
diff --git a/post_proc/equalizer.c b/post_proc/equalizer.c
index 4e4552f..f7d6152 100644
--- a/post_proc/equalizer.c
+++ b/post_proc/equalizer.c
@@ -31,7 +31,7 @@
{0x0bed4300, 0xddd6, 0x11db, 0x8f34, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
{0xa0dac280, 0x401c, 0x11e3, 0x9379, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
EFFECT_CONTROL_API_VERSION,
- (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL),
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL | EFFECT_FLAG_VOLUME_CTRL),
0, /* TODO */
1,
"MSM offload equalizer",
@@ -183,7 +183,7 @@
int equalizer_get_num_presets(equalizer_context_t *context __unused)
{
- ALOGV("%s: presets_num: %d", __func__,
+ ALOGV("%s: presets_num: %zu", __func__,
sizeof(equalizer_preset_names)/sizeof(char *));
return sizeof(equalizer_preset_names)/sizeof(char *);
}
diff --git a/post_proc/reverb.c b/post_proc/reverb.c
index e5fc950..be4d566 100644
--- a/post_proc/reverb.c
+++ b/post_proc/reverb.c
@@ -32,7 +32,7 @@
{ 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e } },
{ 0x79a18026, 0x18fd, 0x4185, 0x8233, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } },
EFFECT_CONTROL_API_VERSION,
- (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_HW_ACC_TUNNEL),
+ (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_HW_ACC_TUNNEL | EFFECT_FLAG_VOLUME_CTRL),
0, /* TODO */
1,
"MSM offload Auxiliary Environmental Reverb",
@@ -44,7 +44,8 @@
{0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}},
{0xeb64ea04, 0x973b, 0x43d2, 0x8f5e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_CONTROL_API_VERSION,
- (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_HW_ACC_TUNNEL),
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_HW_ACC_TUNNEL |
+ EFFECT_FLAG_VOLUME_CTRL),
0, /* TODO */
1,
"MSM offload Insert Environmental Reverb",
@@ -56,7 +57,7 @@
{0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
{0x6987be09, 0xb142, 0x4b41, 0x9056, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_CONTROL_API_VERSION,
- (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_HW_ACC_TUNNEL),
+ (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_HW_ACC_TUNNEL | EFFECT_FLAG_VOLUME_CTRL),
0, /* TODO */
1,
"MSM offload Auxiliary Preset Reverb",
@@ -68,7 +69,8 @@
{0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
{0xaa2bebf6, 0x47cf, 0x4613, 0x9bca, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_CONTROL_API_VERSION,
- (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_HW_ACC_TUNNEL),
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_HW_ACC_TUNNEL |
+ EFFECT_FLAG_VOLUME_CTRL),
0, /* TODO */
1,
"MSM offload Insert Preset Reverb",
diff --git a/post_proc/virtualizer.c b/post_proc/virtualizer.c
index 40bbf38..b9e4fb9 100644
--- a/post_proc/virtualizer.c
+++ b/post_proc/virtualizer.c
@@ -31,7 +31,8 @@
{0x37cc2c00, 0xdddd, 0x11db, 0x8577, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
{0x509a4498, 0x561a, 0x4bea, 0xb3b1, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
EFFECT_CONTROL_API_VERSION,
- (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_HW_ACC_TUNNEL),
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_HW_ACC_TUNNEL |
+ EFFECT_FLAG_VOLUME_CTRL),
0, /* TODO */
1,
"MSM offload virtualizer",
diff --git a/post_proc/volume_listener.c b/post_proc/volume_listener.c
index 1402ae6..5494fd0 100644
--- a/post_proc/volume_listener.c
+++ b/post_proc/volume_listener.c
@@ -26,7 +26,9 @@
#include <cutils/properties.h>
#include <platform_api.h>
-#define PRIMARY_HAL_PATH XSTR(LIB_AUDIO_HAL)
+#define PRIMARY_HAL_FILENAME XSTR(LIB_AUDIO_HAL)
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
#define XSTR(x) STR(x)
#define STR(x) #x
@@ -200,6 +202,10 @@
/* lock must be held when modifying or accessing created_effects_list */
pthread_mutex_t vol_listner_init_lock;
+/* Treblized modules locations */
+static const char *primary_audio_hal_path[] =
+ {"/vendor/lib/hw/", "/system/lib/hw/"};
+
/*
* Local functions
*/
@@ -366,7 +372,9 @@
ALOGE("%s: EFFECT_CMD_INIT: %s, sending -EINVAL", __func__,
(p_reply_data == NULL) ? "p_reply_data is NULL" :
"*reply_size != sizeof(int)");
- return -EINVAL;
+ android_errorWriteLog(0x534e4554, "32669549");
+ status = -EINVAL;
+ goto exit;
}
*(int *)p_reply_data = 0;
break;
@@ -375,7 +383,9 @@
ALOGV("%s :: cmd called EFFECT_CMD_SET_CONFIG", __func__);
if (p_cmd_data == NULL || cmd_size != sizeof(effect_config_t)
|| p_reply_data == NULL || reply_size == NULL || *reply_size != sizeof(int)) {
- return -EINVAL;
+ android_errorWriteLog(0x534e4554, "32669549");
+ status = -EINVAL;
+ goto exit;
}
context->config = *(effect_config_t *)p_cmd_data;
*(int *)p_reply_data = 0;
@@ -399,7 +409,9 @@
ALOGE("%s: EFFECT_CMD_OFFLOAD: %s, sending -EINVAL", __func__,
(p_reply_data == NULL) ? "p_reply_data is NULL" :
"*reply_size != sizeof(int)");
- return -EINVAL;
+ android_errorWriteLog(0x534e4554, "32669549");
+ status = -EINVAL;
+ goto exit;
}
*(int *)p_reply_data = 0;
break;
@@ -555,9 +567,27 @@
return 0;
}
+static bool resolve_audio_hal_path(char *file_name, int mode) {
+ char file_path[PATH_MAX];
+ unsigned long i;
+
+ for (i = 0; i < ARRAY_SIZE(primary_audio_hal_path); i++) {
+ snprintf(file_path, PATH_MAX, "%s/%s",
+ primary_audio_hal_path[i], file_name);
+ if (F_OK == access(file_path, mode)) {
+ strcpy(file_name, file_path);
+ return true;
+ }
+ }
+ return false;
+}
+
static void init_once()
{
int max_table_ent = 0;
+ void *hal_lib_pointer = NULL;
+ char primary_hal_path[PATH_MAX] = {0};
+
if (initialized) {
ALOGV("%s : already init .. do nothing", __func__);
return;
@@ -569,52 +599,54 @@
pthread_mutex_init(&vol_listner_init_lock, NULL);
+ strcpy(primary_hal_path, PRIMARY_HAL_FILENAME);
// get hal function pointer
- if (access(PRIMARY_HAL_PATH, R_OK) == 0) {
- void *hal_lib_pointer = dlopen(PRIMARY_HAL_PATH, RTLD_NOW);
- if (hal_lib_pointer == NULL) {
- ALOGE("%s: DLOPEN failed for %s", __func__, PRIMARY_HAL_PATH);
+ if (resolve_audio_hal_path(primary_hal_path, R_OK)) {
+ hal_lib_pointer = dlopen(primary_hal_path, RTLD_NOW);
+ } else {
+ ALOGE("%s: not able to acces lib: %s", __func__, PRIMARY_HAL_FILENAME);
+ }
+
+ if (!hal_lib_pointer) {
+ ALOGE("%s: DLOPEN failed for %s", __func__, primary_hal_path);
+ } else {
+ ALOGV("%s: DLOPEN of %s Succes .. next get HAL entry function", __func__, primary_hal_path);
+ send_gain_dep_cal = (bool (*)(int))dlsym(hal_lib_pointer, AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION);
+ if (send_gain_dep_cal == NULL) {
+ ALOGE("Couldnt able to get the function symbol");
+ }
+ get_custom_gain_table = (int (*) (struct amp_db_and_gain_table *, int))dlsym(hal_lib_pointer, AHAL_GAIN_GET_MAPPING_TABLE);
+ if (get_custom_gain_table == NULL) {
+ ALOGE("Couldnt able to get the function AHAL_GAIN_GET_MAPPING_TABLE symbol");
} else {
- ALOGV("%s: DLOPEN of %s Succes .. next get HAL entry function", __func__, PRIMARY_HAL_PATH);
- send_gain_dep_cal = (bool (*)(int))dlsym(hal_lib_pointer, AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION);
- if (send_gain_dep_cal == NULL) {
- ALOGE("Couldnt able to get the function symbol");
- }
- get_custom_gain_table = (int (*) (struct amp_db_and_gain_table *, int))dlsym(hal_lib_pointer, AHAL_GAIN_GET_MAPPING_TABLE);
- if (get_custom_gain_table == NULL) {
- ALOGE("Couldnt able to get the function AHAL_GAIN_GET_MAPPING_TABLE symbol");
- } else {
- max_table_ent = get_custom_gain_table(volume_curve_gain_mapping_table, MAX_VOLUME_CAL_STEPS);
- // if number of entries is 0 use default
- // if number of entries > MAX_VOLUME_CAL_STEPS (this should never happen) then in this case
- // use only default number of steps but this will result in unexpected behaviour
+ max_table_ent = get_custom_gain_table(volume_curve_gain_mapping_table, MAX_VOLUME_CAL_STEPS);
+ // if number of entries is 0 use default
+ // if number of entries > MAX_VOLUME_CAL_STEPS (this should never happen) then in this case
+ // use only default number of steps but this will result in unexpected behaviour
- if (max_table_ent > 0 && max_table_ent <= MAX_VOLUME_CAL_STEPS) {
- if (max_table_ent < total_volume_cal_step) {
- for (int i = max_table_ent; i < total_volume_cal_step; i++ ) {
- volume_curve_gain_mapping_table[i].amp = 0;
- volume_curve_gain_mapping_table[i].db = 0;
- volume_curve_gain_mapping_table[i].level = -1;
- }
+ if (max_table_ent > 0 && max_table_ent <= MAX_VOLUME_CAL_STEPS) {
+ if (max_table_ent < total_volume_cal_step) {
+ for (int i = max_table_ent; i < total_volume_cal_step; i++ ) {
+ volume_curve_gain_mapping_table[i].amp = 0;
+ volume_curve_gain_mapping_table[i].db = 0;
+ volume_curve_gain_mapping_table[i].level = -1;
}
- total_volume_cal_step = max_table_ent;
- ALOGD("%s: using custome volume table", __func__);
- } else {
- ALOGD("%s: using default volume table", __func__);
}
+ total_volume_cal_step = max_table_ent;
+ ALOGD("%s: using custome volume table", __func__);
+ } else {
+ ALOGD("%s: using default volume table", __func__);
+ }
- if (dumping_enabled) {
- ALOGD("%s: dumping table here .. size of table received %d",
- __func__, max_table_ent);
- for (int i = 0; i < MAX_VOLUME_CAL_STEPS ; i++)
- ALOGD("[%d] %f %f %d", i, volume_curve_gain_mapping_table[i].amp,
- volume_curve_gain_mapping_table[i].db,
- volume_curve_gain_mapping_table[i].level);
- }
+ if (dumping_enabled) {
+ ALOGD("%s: dumping table here .. size of table received %d",
+ __func__, max_table_ent);
+ for (int i = 0; i < MAX_VOLUME_CAL_STEPS ; i++)
+ ALOGD("[%d] %f %f %d", i, volume_curve_gain_mapping_table[i].amp,
+ volume_curve_gain_mapping_table[i].db,
+ volume_curve_gain_mapping_table[i].level);
}
}
- } else {
- ALOGE("%s: not able to acces lib %s ", __func__, PRIMARY_HAL_PATH);
}
// check system property to see if dumping is required
diff --git a/visualizer/Android.mk b/visualizer/Android.mk
index 87d4987..e318214 100644
--- a/visualizer/Android.mk
+++ b/visualizer/Android.mk
@@ -29,6 +29,8 @@
LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_MODULE:= libqcomvisualizer
+LOCAL_MODULE_OWNER := qcom
+LOCAL_PROPRIETARY_MODULE := true
LOCAL_C_INCLUDES := \
external/tinyalsa/include \
diff --git a/visualizer/offload_visualizer.c b/visualizer/offload_visualizer.c
index c56ebff..4d1373f 100644
--- a/visualizer/offload_visualizer.c
+++ b/visualizer/offload_visualizer.c
@@ -81,11 +81,11 @@
effect_ops_t ops;
};
-typedef struct output_context_s {
+struct output_context_s {
struct listnode outputs_list_node; /* node in active_outputs_list */
audio_io_handle_t handle; /* io handle */
struct listnode effects_list; /* list of effects attached to this output */
-} output_context_t;
+};
/* maximum time since last capture buffer update before resetting capture buffer. This means
@@ -427,7 +427,7 @@
__attribute__ ((visibility ("default")))
int visualizer_hal_start_output(audio_io_handle_t output, int pcm_id) {
- int ret;
+ int ret = 0;
struct listnode *node;
ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
@@ -473,7 +473,7 @@
__attribute__ ((visibility ("default")))
int visualizer_hal_stop_output(audio_io_handle_t output, int pcm_id) {
- int ret;
+ int ret = 0;
struct listnode *node;
struct listnode *fx_node;
output_context_t *out_ctxt;
diff --git a/voice_processing/Android.mk b/voice_processing/Android.mk
index 9b86eaf..e8878ee 100644
--- a/voice_processing/Android.mk
+++ b/voice_processing/Android.mk
@@ -5,6 +5,8 @@
LOCAL_MODULE:= libqcomvoiceprocessing
LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_OWNER := qcom
+LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := soundfx
LOCAL_SRC_FILES:= \
@@ -14,6 +16,7 @@
$(call include-path-for, audio-effects)
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils
LOCAL_SHARED_LIBRARIES += libdl
diff --git a/voice_processing/voice_processing.c b/voice_processing/voice_processing.c
index be3d801..d2b4acb 100644
--- a/voice_processing/voice_processing.c
+++ b/voice_processing/voice_processing.c
@@ -30,7 +30,8 @@
// local definitions
//------------------------------------------------------------------------------
-#define EFFECTS_DESCRIPTOR_LIBRARY_PATH "/system/lib/soundfx/libqcomvoiceprocessingdescriptors.so"
+#define EFFECTS_DESCRIPTOR_LIBRARY_PATH "/vendor/lib/soundfx/libqcomvoiceprocessingdescriptors.so"
+#define EFFECTS_DESCRIPTOR_LIBRARY_PATH2 "/system/lib/soundfx/libqcomvoiceprocessingdescriptors.so"
// types of pre processing modules
enum effect_id
@@ -426,12 +427,20 @@
if (init_status <= 0)
return init_status;
- if (access(EFFECTS_DESCRIPTOR_LIBRARY_PATH, R_OK) == 0) {
- lib_handle = dlopen(EFFECTS_DESCRIPTOR_LIBRARY_PATH, RTLD_NOW);
+ const char *path = EFFECTS_DESCRIPTOR_LIBRARY_PATH;
+ int result = access(path, R_OK);
+
+ if (result != 0) {
+ path = EFFECTS_DESCRIPTOR_LIBRARY_PATH2;
+ result = access(path, R_OK);
+ }
+
+ if (result == 0) {
+ lib_handle = dlopen(path, RTLD_NOW);
if (lib_handle == NULL) {
- ALOGE("%s: DLOPEN failed for %s", __func__, EFFECTS_DESCRIPTOR_LIBRARY_PATH);
+ ALOGE("%s: DLOPEN failed for %s", __func__, path);
} else {
- ALOGV("%s: DLOPEN successful for %s", __func__, EFFECTS_DESCRIPTOR_LIBRARY_PATH);
+ ALOGV("%s: DLOPEN successful for %s", __func__, path);
desc = (const effect_descriptor_t *)dlsym(lib_handle,
"qcom_product_aec_descriptor");
if (desc)
@@ -448,6 +457,8 @@
// if (desc)
// descriptors[AGC_ID] = desc;
}
+ } else {
+ ALOGE("%s: can't find %s", __func__, path);
}
uuid_to_id_table[AEC_ID] = FX_IID_AEC;