upload android base code part4

This commit is contained in:
August 2018-08-08 17:00:29 +08:00
parent b9e30e05b1
commit 78ea2404cd
23455 changed files with 5250148 additions and 0 deletions

View file

@ -0,0 +1,162 @@
/*
* Copyright (C) 2015 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_extn"
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
#include <stdlib.h>
#include <errno.h>
#include <dlfcn.h>
#include <cutils/properties.h>
#include <cutils/log.h>
#include "audio_hw.h"
#include "audio_extn.h"
#include "platform.h"
#include "platform_api.h"
struct snd_card_split cur_snd_card_split = {
.device = {0},
.snd_card = {0},
.form_factor = {0},
};
struct snd_card_split *audio_extn_get_snd_card_split()
{
return &cur_snd_card_split;
}
void audio_extn_set_snd_card_split(const char* in_snd_card_name)
{
/* sound card name follows below mentioned convention
<target name>-<sound card name>-<form factor>-snd-card
parse target name, sound card name and form factor
*/
char *snd_card_name = strdup(in_snd_card_name);
char *tmp = NULL;
char *device = NULL;
char *snd_card = NULL;
char *form_factor = NULL;
if (in_snd_card_name == NULL) {
ALOGE("%s: snd_card_name passed is NULL", __func__);
goto on_error;
}
device = strtok_r(snd_card_name, "-", &tmp);
if (device == NULL) {
ALOGE("%s: called on invalid snd card name", __func__);
goto on_error;
}
strlcpy(cur_snd_card_split.device, device, HW_INFO_ARRAY_MAX_SIZE);
snd_card = strtok_r(NULL, "-", &tmp);
if (snd_card == NULL) {
ALOGE("%s: called on invalid snd card name", __func__);
goto on_error;
}
strlcpy(cur_snd_card_split.snd_card, snd_card, HW_INFO_ARRAY_MAX_SIZE);
form_factor = strtok_r(NULL, "-", &tmp);
if (form_factor == NULL) {
ALOGE("%s: called on invalid snd card name", __func__);
goto on_error;
}
strlcpy(cur_snd_card_split.form_factor, form_factor, HW_INFO_ARRAY_MAX_SIZE);
ALOGI("%s: snd_card_name(%s) device(%s) snd_card(%s) form_factor(%s)",
__func__, in_snd_card_name, device, snd_card, form_factor);
on_error:
if (snd_card_name)
free(snd_card_name);
}
#ifdef KPI_OPTIMIZE_ENABLED
typedef int (*perf_lock_acquire_t)(int, int, int*, int);
typedef int (*perf_lock_release_t)(int);
static void *qcopt_handle;
static perf_lock_acquire_t perf_lock_acq;
static perf_lock_release_t perf_lock_rel;
static int perf_lock_handle;
char opt_lib_path[PROPERTY_VALUE_MAX] = {0};
int perf_lock_opts[] = {0x101, 0x20E, 0x30E};
int audio_extn_perf_lock_init(void)
{
int ret = 0;
if (qcopt_handle == NULL) {
if (property_get("ro.vendor.extension_library",
opt_lib_path, NULL) <= 0) {
ALOGE("%s: Failed getting perf property", __func__);
ret = -EINVAL;
goto err;
}
if ((qcopt_handle = dlopen(opt_lib_path, RTLD_NOW)) == NULL) {
ALOGE("%s: Failed to open perf handle", __func__);
ret = -EINVAL;
goto err;
} else {
perf_lock_acq = (perf_lock_acquire_t)dlsym(qcopt_handle,
"perf_lock_acq");
if (perf_lock_acq == NULL) {
ALOGE("%s: Perf lock Acquire NULL", __func__);
dlclose(qcopt_handle);
qcopt_handle = NULL;
ret = -EINVAL;
goto err;
}
perf_lock_rel = (perf_lock_release_t)dlsym(qcopt_handle,
"perf_lock_rel");
if (perf_lock_rel == NULL) {
ALOGE("%s: Perf lock Release NULL", __func__);
dlclose(qcopt_handle);
qcopt_handle = NULL;
ret = -EINVAL;
goto err;
}
ALOGD("%s: Perf lock handles Success", __func__);
}
}
err:
return ret;
}
void audio_extn_perf_lock_acquire(void)
{
if (perf_lock_acq) {
perf_lock_handle = perf_lock_acq(perf_lock_handle, 0, perf_lock_opts, 3);
ALOGV("%s: Perf lock acquired", __func__);
} else {
ALOGE("%s: Perf lock acquire error", __func__);
}
}
void audio_extn_perf_lock_release(void)
{
if (perf_lock_rel && perf_lock_handle) {
perf_lock_rel(perf_lock_handle);
perf_lock_handle = 0;
ALOGV("%s: Perf lock released", __func__);
} else {
ALOGE("%s: Perf lock release error", __func__);
}
}
#endif /* KPI_OPTIMIZE_ENABLED */

View file

@ -0,0 +1,198 @@
/*
* Copyright (C) 2014 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.
*/
#ifndef AUDIO_EXTN_H
#define AUDIO_EXTN_H
#include <cutils/str_parms.h>
#define HW_INFO_ARRAY_MAX_SIZE 32
struct snd_card_split {
char device[HW_INFO_ARRAY_MAX_SIZE];
char snd_card[HW_INFO_ARRAY_MAX_SIZE];
char form_factor[HW_INFO_ARRAY_MAX_SIZE];
};
void *audio_extn_extspk_init(struct audio_device *adev);
void audio_extn_extspk_deinit(void *extn);
void audio_extn_extspk_update(void* extn);
void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode);
void audio_extn_extspk_set_voice_vol(void* extn, float vol);
struct snd_card_split *audio_extn_get_snd_card_split();
void audio_extn_set_snd_card_split(const char* in_snd_card_name);
#ifndef SPKR_PROT_ENABLED
#define audio_extn_spkr_prot_init(adev) (0)
#define audio_extn_spkr_prot_start_processing(snd_device) (-EINVAL)
#define audio_extn_spkr_prot_calib_cancel(adev) (0)
#define audio_extn_spkr_prot_stop_processing(snd_device) (0)
#define audio_extn_spkr_prot_is_enabled() (false)
#define audio_extn_spkr_prot_get_acdb_id(snd_device) (-EINVAL)
#define audio_extn_get_spkr_prot_snd_device(snd_device) (snd_device)
#else
void audio_extn_spkr_prot_init(void *adev);
int audio_extn_spkr_prot_start_processing(snd_device_t snd_device);
void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device);
bool audio_extn_spkr_prot_is_enabled();
int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device);
int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device);
void audio_extn_spkr_prot_calib_cancel(void *adev);
#endif
#ifndef HFP_ENABLED
#define audio_extn_hfp_is_active(adev) (0)
#define audio_extn_hfp_get_usecase() (-1)
#define audio_extn_hfp_set_parameters(adev, params) (0)
#define audio_extn_hfp_set_mic_mute(adev, state) (0)
#else
bool audio_extn_hfp_is_active(struct audio_device *adev);
audio_usecase_t audio_extn_hfp_get_usecase();
void audio_extn_hfp_set_parameters(struct audio_device *adev,
struct str_parms *parms);
int audio_extn_hfp_set_mic_mute(struct audio_device *adev, bool state);
#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)
#define audio_extn_usb_get_max_channels(dir) (0)
#define audio_extn_usb_get_max_bit_width(dir) (0)
#define audio_extn_usb_sup_sample_rates(t, s, l) (0)
#define audio_extn_usb_alive(adev) (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();
int audio_extn_usb_get_max_channels(bool is_playback);
int audio_extn_usb_get_max_bit_width(bool is_playback);
int audio_extn_usb_sup_sample_rates(bool is_playback, uint32_t *sr, uint32_t l);
bool audio_extn_usb_alive(int card);
#endif
#ifndef SOUND_TRIGGER_ENABLED
#define audio_extn_sound_trigger_init(adev) (0)
#define audio_extn_sound_trigger_deinit(adev) (0)
#define audio_extn_sound_trigger_update_device_status(snd_dev, event) (0)
#define audio_extn_sound_trigger_set_parameters(adev, parms) (0)
#define audio_extn_sound_trigger_check_and_get_session(in) (0)
#define audio_extn_sound_trigger_stop_lab(in) (0)
#define audio_extn_sound_trigger_read(in, buffer, bytes) (0)
#else
enum st_event_type {
ST_EVENT_SND_DEVICE_FREE,
ST_EVENT_SND_DEVICE_BUSY,
ST_EVENT_STREAM_FREE,
ST_EVENT_STREAM_BUSY
};
typedef enum st_event_type st_event_type_t;
int audio_extn_sound_trigger_init(struct audio_device *adev);
void audio_extn_sound_trigger_deinit(struct audio_device *adev);
void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
st_event_type_t event);
void audio_extn_sound_trigger_set_parameters(struct audio_device *adev,
struct str_parms *parms);
void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in);
void audio_extn_sound_trigger_stop_lab(struct stream_in *in);
int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
size_t bytes);
#endif
#ifndef DSM_FEEDBACK_ENABLED
#define audio_extn_dsm_feedback_enable(adev, snd_device, benable) (0)
#else
void audio_extn_dsm_feedback_enable(struct audio_device *adev,
snd_device_t snd_device,
bool benable);
#endif
void audio_extn_utils_send_default_app_type_cfg(void *platform, struct mixer *mixer);
int audio_extn_utils_send_app_type_cfg(struct audio_device *adev,
struct audio_usecase *usecase);
void audio_extn_utils_send_audio_calibration(struct audio_device *adev,
struct audio_usecase *usecase);
int audio_extn_utils_send_app_type_gain(struct audio_device *adev,
int app_type,
int *gain);
#ifndef HWDEP_CAL_ENABLED
#define audio_extn_hwdep_cal_send(snd_card, acdb_handle) (0)
#else
void audio_extn_hwdep_cal_send(int snd_card, void *acdb_handle);
#endif
#ifndef KPI_OPTIMIZE_ENABLED
#define audio_extn_perf_lock_init() (0)
#define audio_extn_perf_lock_acquire() (0)
#define audio_extn_perf_lock_release() (0)
#else
int audio_extn_perf_lock_init(void);
void audio_extn_perf_lock_acquire(void);
void audio_extn_perf_lock_release(void);
#endif /* KPI_OPTIMIZE_ENABLED */
#ifndef HW_VARIANTS_ENABLED
#define hw_info_init(snd_card_name) (0)
#define hw_info_deinit(hw_info) (0)
#define hw_info_append_hw_type(hw_info,\
snd_device, device_name) (0)
#else
void *hw_info_init(const char *snd_card_name);
void hw_info_deinit(void *hw_info);
void hw_info_append_hw_type(void *hw_info, snd_device_t snd_device,
char *device_name);
#endif /* HW_VARIANTS_ENABLED */
typedef void (* snd_mon_cb)(void * stream, struct str_parms * parms);
#ifndef SND_MONITOR_ENABLED
#define audio_extn_snd_mon_init() (0)
#define audio_extn_snd_mon_deinit() (0)
#define audio_extn_snd_mon_register_listener(stream, cb) (0)
#define audio_extn_snd_mon_unregister_listener(stream) (0)
#else
int audio_extn_snd_mon_init();
int audio_extn_snd_mon_deinit();
int audio_extn_snd_mon_register_listener(void *stream, snd_mon_cb cb);
int audio_extn_snd_mon_unregister_listener(void *stream);
#endif
bool audio_extn_utils_resolve_config_file(char[]);
int audio_extn_utils_get_platform_info(const char* snd_card_name,
char* platform_info_file);
int audio_extn_utils_get_snd_card_num();
#endif /* AUDIO_EXTN_H */

View file

@ -0,0 +1,105 @@
/*
* Copyright (C) 2015 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_dsm_feedback"
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
#include <errno.h>
#include <math.h>
#include <cutils/log.h>
#include "audio_hw.h"
#include "platform.h"
#include "platform_api.h"
#include <stdlib.h>
static struct pcm_config pcm_config_dsm = {
.channels = 2,
.rate = 48000,
.period_size = 256,
.period_count = 4,
.format = PCM_FORMAT_S16_LE,
.start_threshold = 0,
.stop_threshold = INT_MAX,
.avail_min = 0,
};
int start_dsm_feedback_processing(struct audio_device *adev, int enable)
{
int ret = 0;
int32_t pcm_dev_tx_id = -1;
static struct pcm *dsm_pcm_handle = NULL;
if (enable) {
/*do nothing if already enabled*/
if (dsm_pcm_handle)
return ret;
pcm_dev_tx_id = platform_get_pcm_device_id(USECASE_AUDIO_DSM_FEEDBACK, PCM_CAPTURE);
if (pcm_dev_tx_id < 0) {
ALOGE("%s: Invalid pcm device for usecase (%d)",
__func__, USECASE_AUDIO_DSM_FEEDBACK);
ret = -ENODEV;
goto close;
}
dsm_pcm_handle = pcm_open(adev->snd_card,
pcm_dev_tx_id,
PCM_IN, &pcm_config_dsm);
if (dsm_pcm_handle && !pcm_is_ready(dsm_pcm_handle)) {
ALOGE("%s: %s", __func__, pcm_get_error(dsm_pcm_handle));
ret = -EIO;
goto close;
}
if (pcm_start(dsm_pcm_handle) < 0) {
ALOGE("%s: pcm start for RX failed", __func__);
ret = -EINVAL;
goto close;
}
return ret;
}
close:
/*close pcm if disable or error happend in opening*/
if (dsm_pcm_handle) {
pcm_close(dsm_pcm_handle);
dsm_pcm_handle = NULL;
}
return ret;
}
void audio_extn_dsm_feedback_enable(struct audio_device *adev,
snd_device_t snd_device,
int benable)
{
if ( NULL == adev )
return;
if( snd_device == SND_DEVICE_OUT_SPEAKER ||
snd_device == SND_DEVICE_OUT_SPEAKER_REVERSE ||
snd_device == SND_DEVICE_OUT_VOICE_SPEAKER ||
snd_device == SND_DEVICE_OUT_SPEAKER_SAFE ||
snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES ||
snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE ||
snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES ||
snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE )
start_dsm_feedback_processing(adev, benable);
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (C) 2014 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 "ext_speaker"
/*#define LOG_NDEBUG 0*/
#include <cutils/log.h>
#include <stdlib.h>
#include <audio_hw.h>
#include <dlfcn.h>
#ifdef __LP64__
#define LIB_SPEAKER_BUNDLE "/vendor/lib64/soundfx/libspeakerbundle.so"
#else
#define LIB_SPEAKER_BUNDLE "/vendor/lib/soundfx/libspeakerbundle.so"
#endif
typedef void (*set_mode_t)(int);
typedef void (*set_speaker_on_t)(bool);
typedef void (*set_earpiece_on_t)(bool);
typedef void (*set_voice_vol_t)(float);
struct speaker_data {
struct audio_device *adev;
void *speaker_bundle;
set_mode_t set_mode;
set_speaker_on_t set_speaker_on;
set_earpiece_on_t set_earpiece_on;
set_voice_vol_t set_voice_vol;
};
static struct speaker_data* open_speaker_bundle()
{
struct speaker_data *sd = calloc(1, sizeof(struct speaker_data));
sd->speaker_bundle = dlopen(LIB_SPEAKER_BUNDLE, RTLD_NOW);
if (sd->speaker_bundle == NULL) {
ALOGE("%s: DLOPEN failed for %s", __func__, LIB_SPEAKER_BUNDLE);
goto error;
} else {
ALOGV("%s: DLOPEN successful for %s", __func__, LIB_SPEAKER_BUNDLE);
sd->set_mode = (set_mode_t)dlsym(sd->speaker_bundle,
"set_mode");
if (sd->set_mode == NULL) {
ALOGE("%s: dlsym error %s for set_mode", __func__,
dlerror());
goto error;
}
sd->set_speaker_on = (set_speaker_on_t)dlsym(sd->speaker_bundle,
"set_speaker_on");
if (sd->set_speaker_on == NULL) {
ALOGE("%s: dlsym error %s for set_speaker_on", __func__,
dlerror());
goto error;
}
sd->set_earpiece_on = (set_earpiece_on_t)dlsym(sd->speaker_bundle,
"set_earpiece_on");
if (sd->set_earpiece_on == NULL) {
ALOGE("%s: dlsym error %s for set_earpiece_on", __func__,
dlerror());
goto error;
}
sd->set_voice_vol = (set_voice_vol_t)dlsym(sd->speaker_bundle,
"set_voice_volume");
if (sd->set_voice_vol == NULL) {
ALOGE("%s: dlsym error %s for set_voice_volume",
__func__, dlerror());
goto error;
}
}
return sd;
error:
free(sd);
return 0;
}
static void close_speaker_bundle(struct speaker_data *sd)
{
if (sd != NULL) {
dlclose(sd->speaker_bundle);
free(sd);
sd = NULL;
}
}
void *audio_extn_extspk_init(struct audio_device *adev)
{
struct speaker_data *data = open_speaker_bundle();
if (data)
data->adev = adev;
return data;
}
void audio_extn_extspk_deinit(void *extn)
{
struct speaker_data *data = (struct speaker_data*)extn;
close_speaker_bundle(data);
}
void audio_extn_extspk_update(void* extn)
{
struct speaker_data *data = (struct speaker_data*)extn;
if (data) {
bool speaker_on = false;
bool earpiece_on = false;
struct listnode *node;
struct audio_usecase *usecase;
list_for_each(node, &data->adev->usecase_list) {
usecase = node_to_item(node, struct audio_usecase, list);
if (usecase->devices & AUDIO_DEVICE_OUT_EARPIECE) {
if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
earpiece_on = true;
}
}
if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) {
if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
speaker_on = true;
}
}
}
data->set_earpiece_on(earpiece_on);
data->set_speaker_on(speaker_on);
}
}
void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode)
{
struct speaker_data *data = (struct speaker_data*)extn;
if (data)
data->set_mode(mode);
}
void audio_extn_extspk_set_voice_vol(void* extn, float vol)
{
struct speaker_data *data = (struct speaker_data*)extn;
if (data)
data->set_voice_vol(vol);
}

View file

@ -0,0 +1,509 @@
/*
* Copyright (C) 2014 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_hfp"
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
#include <errno.h>
#include <math.h>
#include <cutils/log.h>
#include "audio_hw.h"
#include "platform.h"
#include "platform_api.h"
#include <stdlib.h>
#include <cutils/str_parms.h>
#include "audio_extn/tfa_98xx.h"
#include "audio_extn.h"
#define AUDIO_PARAMETER_HFP_ENABLE "hfp_enable"
#define AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate"
#define AUDIO_PARAMETER_KEY_HFP_VOLUME "hfp_volume"
#define AUDIO_PARAMETER_HFP_VOL_MIXER_CTL "hfp_vol_mixer_ctl"
#define AUDIO_PARAMATER_HFP_VALUE_MAX 128
#define AUDIO_PARAMETER_KEY_HFP_MIC_VOLUME "hfp_mic_volume"
#define PLAYBACK_VOLUME_MAX 0x2000
#define CAPTURE_VOLUME_DEFAULT (15.0)
static int32_t start_hfp(struct audio_device *adev,
struct str_parms *parms);
static int32_t stop_hfp(struct audio_device *adev);
struct hfp_module {
struct pcm *hfp_sco_rx;
struct pcm *hfp_sco_tx;
struct pcm *hfp_pcm_rx;
struct pcm *hfp_pcm_tx;
float hfp_volume;
float mic_volume;
char hfp_vol_mixer_ctl[AUDIO_PARAMATER_HFP_VALUE_MAX];
bool is_hfp_running;
bool mic_mute;
audio_usecase_t ucid;
};
static struct hfp_module hfpmod = {
.hfp_sco_rx = NULL,
.hfp_sco_tx = NULL,
.hfp_pcm_rx = NULL,
.hfp_pcm_tx = NULL,
.hfp_volume = 0,
.mic_volume = CAPTURE_VOLUME_DEFAULT,
.hfp_vol_mixer_ctl = {0, },
.is_hfp_running = 0,
.mic_mute = 0,
.ucid = USECASE_AUDIO_HFP_SCO,
};
static struct pcm_config pcm_config_hfp = {
.channels = 1,
.rate = 8000,
.period_size = 240,
.period_count = 2,
.format = PCM_FORMAT_S16_LE,
.start_threshold = 0,
.stop_threshold = INT_MAX,
.avail_min = 0,
};
static int32_t hfp_set_volume(struct audio_device *adev, float value)
{
int32_t vol, ret = 0;
struct mixer_ctl *ctl;
ALOGV("%s: entry", __func__);
ALOGD("%s: (%f)\n", __func__, value);
hfpmod.hfp_volume = value;
audio_extn_tfa_98xx_set_voice_vol(value);
if (value < 0.0) {
ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
value = 0.0;
} else {
value = ((value > 15.000000) ? 1.0 : (value / 15));
ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
}
vol = lrint((value * 0x2000) + 0.5);
if (!hfpmod.is_hfp_running) {
ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
return -EIO;
}
ALOGD("%s: Setting HFP volume to %d \n", __func__, vol);
if (0 == hfpmod.hfp_vol_mixer_ctl[0]) {
#ifdef EXTERNAL_BT_SUPPORTED
strcpy(hfpmod.hfp_vol_mixer_ctl, "PRI AUXPCM LOOPBACK Volume");
#else
strcpy(hfpmod.hfp_vol_mixer_ctl, "Internal HFP RX Volume");
#endif
ALOGW("%s: Defaulting hfp mixer control to: %s",
__func__, hfpmod.hfp_vol_mixer_ctl);
}
ctl = mixer_get_ctl_by_name(adev->mixer, hfpmod.hfp_vol_mixer_ctl);
if (!ctl) {
ALOGE("%s: Could not get ctl for mixer cmd - %s",
__func__, hfpmod.hfp_vol_mixer_ctl);
return -EINVAL;
}
if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, vol);
return -EINVAL;
}
ALOGV("%s: exit", __func__);
return ret;
}
/*Set mic volume to value.
*
* This interface is used for mic volume control, set mic volume as value(range 0 ~ 15).
*/
static int hfp_set_mic_volume(struct audio_device *adev, float value)
{
int volume, ret = 0;
char mixer_ctl_name[128];
struct mixer_ctl *ctl;
int pcm_device_id = HFP_ASM_RX_TX;
if (!hfpmod.is_hfp_running) {
ALOGE("%s: HFP not active, ignoring set_hfp_mic_volume call", __func__);
return -EIO;
}
if (value < 0.0) {
ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
value = 0.0;
} else if (value > CAPTURE_VOLUME_DEFAULT) {
value = CAPTURE_VOLUME_DEFAULT;
ALOGW("%s: Volume brought within range (%f)\n", __func__, value);
}
value = value / CAPTURE_VOLUME_DEFAULT;
memset(mixer_ctl_name, 0, sizeof(mixer_ctl_name));
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
"Playback %d Volume", pcm_device_id);
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
if (!ctl) {
ALOGE("%s: Could not get ctl for mixer cmd - %s",
__func__, mixer_ctl_name);
return -EINVAL;
}
volume = (int)(value * PLAYBACK_VOLUME_MAX);
ALOGD("%s: Setting volume to %d (%s)\n", __func__, volume, mixer_ctl_name);
if (mixer_ctl_set_value(ctl, 0, volume) < 0) {
ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, volume);
return -EINVAL;
}
return ret;
}
static float hfp_get_mic_volume(struct audio_device *adev)
{
int volume, ret = 0;
char mixer_ctl_name[128];
struct mixer_ctl *ctl;
int pcm_device_id = HFP_ASM_RX_TX;
float value = 0.0;
if (!hfpmod.is_hfp_running) {
ALOGE("%s: HFP not active, ignoring set_hfp_mic_volume call", __func__);
return -EIO;
}
memset(mixer_ctl_name, 0, sizeof(mixer_ctl_name));
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
"Playback %d Volume", pcm_device_id);
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
if (!ctl) {
ALOGE("%s: Could not get ctl for mixer cmd - %s",
__func__, mixer_ctl_name);
return -EINVAL;
}
volume = mixer_ctl_get_value(ctl, 0);
if ( volume < 0) {
ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, volume);
return -EINVAL;
}
ALOGD("%s: getting mic volume %d \n", __func__, volume);
value = (volume / PLAYBACK_VOLUME_MAX) * CAPTURE_VOLUME_DEFAULT;
if (value < 0.0) {
ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
value = 0.0;
} else if (value > CAPTURE_VOLUME_DEFAULT) {
value = CAPTURE_VOLUME_DEFAULT;
ALOGW("%s: Volume brought within range (%f)\n", __func__, value);
}
return value;
}
/*Set mic mute state.
*
* This interface is used for mic mute state control
*/
int audio_extn_hfp_set_mic_mute(struct audio_device *adev, bool state)
{
int rc = 0;
if (state == hfpmod.mic_mute)
return rc;
if (state == true) {
hfpmod.mic_volume = hfp_get_mic_volume(adev);
}
rc = hfp_set_mic_volume(adev, (state == true) ? 0.0 : hfpmod.mic_volume);
adev->voice.mic_mute = state;
hfpmod.mic_mute = state;
ALOGD("%s: Setting mute state %d, rc %d\n", __func__, state, rc);
return rc;
}
static int32_t start_hfp(struct audio_device *adev,
struct str_parms *parms __unused)
{
int32_t i, ret = 0;
struct audio_usecase *uc_info;
int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
ALOGD("%s: enter", __func__);
if (adev->enable_hfp == true) {
ALOGD("%s: HFP is already active!\n", __func__);
return 0;
}
adev->enable_hfp = true;
platform_set_mic_mute(adev->platform, false);
uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
uc_info->id = hfpmod.ucid;
uc_info->type = PCM_HFP_CALL;
uc_info->stream.out = adev->primary_output;
uc_info->devices = adev->primary_output->devices;
uc_info->in_snd_device = SND_DEVICE_NONE;
uc_info->out_snd_device = SND_DEVICE_NONE;
list_add_tail(&adev->usecase_list, &uc_info->list);
audio_extn_tfa_98xx_set_mode_bt();
select_devices(adev, hfpmod.ucid);
pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
__func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
ret = -EIO;
goto exit;
}
ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
__func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
__func__, adev->snd_card, pcm_dev_rx_id);
hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
pcm_dev_asm_rx_id,
PCM_OUT, &pcm_config_hfp);
if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
ret = -EIO;
goto exit;
}
ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
__func__, adev->snd_card, pcm_dev_tx_id);
if (audio_extn_tfa_98xx_is_supported() == false) {
hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
pcm_dev_rx_id,
PCM_OUT, &pcm_config_hfp);
if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
ret = -EIO;
goto exit;
}
}
hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
pcm_dev_asm_tx_id,
PCM_IN, &pcm_config_hfp);
if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
ret = -EIO;
goto exit;
}
ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
__func__, adev->snd_card, pcm_dev_tx_id);
if (audio_extn_tfa_98xx_is_supported() == false) {
hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
pcm_dev_tx_id,
PCM_IN, &pcm_config_hfp);
if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
ret = -EIO;
goto exit;
}
}
pcm_start(hfpmod.hfp_sco_rx);
pcm_start(hfpmod.hfp_sco_tx);
if (audio_extn_tfa_98xx_is_supported() == false) {
pcm_start(hfpmod.hfp_pcm_rx);
pcm_start(hfpmod.hfp_pcm_tx);
}
audio_extn_tfa_98xx_enable_speaker();
hfpmod.is_hfp_running = true;
hfp_set_volume(adev, hfpmod.hfp_volume);
/* Set mic volume by mute status, we don't provide set mic volume in phone app, only
provide mute and unmute. */
audio_extn_hfp_set_mic_mute(adev, adev->mic_muted);
ALOGD("%s: exit: status(%d)", __func__, ret);
return 0;
exit:
stop_hfp(adev);
ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
return ret;
}
static int32_t stop_hfp(struct audio_device *adev)
{
int32_t i, ret = 0;
struct audio_usecase *uc_info;
ALOGD("%s: enter", __func__);
hfpmod.is_hfp_running = false;
/* 1. Close the PCM devices */
if (hfpmod.hfp_sco_rx) {
pcm_close(hfpmod.hfp_sco_rx);
hfpmod.hfp_sco_rx = NULL;
}
if (hfpmod.hfp_sco_tx) {
pcm_close(hfpmod.hfp_sco_tx);
hfpmod.hfp_sco_tx = NULL;
}
if (hfpmod.hfp_pcm_rx) {
pcm_close(hfpmod.hfp_pcm_rx);
hfpmod.hfp_pcm_rx = NULL;
}
if (hfpmod.hfp_pcm_tx) {
pcm_close(hfpmod.hfp_pcm_tx);
hfpmod.hfp_pcm_tx = NULL;
}
uc_info = get_usecase_from_list(adev, hfpmod.ucid);
if (uc_info == NULL) {
ALOGE("%s: Could not find the usecase (%d) in the list",
__func__, hfpmod.ucid);
return -EINVAL;
}
/* 2. Get and set stream specific mixer controls */
disable_audio_route(adev, uc_info);
/* 3. Disable the rx and tx devices */
disable_snd_device(adev, uc_info->out_snd_device);
disable_snd_device(adev, uc_info->in_snd_device);
/* Disable the echo reference for HFP Tx */
platform_set_echo_reference(adev, false, AUDIO_DEVICE_NONE);
/* Set the unmute Tx mixer control */
if (voice_get_mic_mute(adev)) {
platform_set_mic_mute(adev->platform, false);
ALOGD("%s: unMute HFP Tx", __func__);
}
adev->enable_hfp = false;
list_remove(&uc_info->list);
free(uc_info);
ALOGD("%s: exit: status(%d)", __func__, ret);
return ret;
}
bool audio_extn_hfp_is_active(struct audio_device *adev)
{
struct audio_usecase *hfp_usecase = NULL;
hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid);
if (hfp_usecase != NULL)
return true;
else
return false;
}
audio_usecase_t audio_extn_hfp_get_usecase()
{
return hfpmod.ucid;
}
void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
{
int ret;
int rate;
int val;
float vol;
char value[AUDIO_PARAMATER_HFP_VALUE_MAX] = {0, };
ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
sizeof(value));
if (ret >= 0) {
if (!strncmp(value,"true",sizeof(value)))
ret = start_hfp(adev,parms);
else
stop_hfp(adev);
}
memset(value, 0, sizeof(value));
ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
sizeof(value));
if (ret >= 0) {
rate = atoi(value);
if (rate == 8000){
hfpmod.ucid = USECASE_AUDIO_HFP_SCO;
pcm_config_hfp.rate = rate;
} else if (rate == 16000){
hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB;
pcm_config_hfp.rate = rate;
} else
ALOGE("Unsupported rate..");
}
if (hfpmod.is_hfp_running) {
memset(value, 0, sizeof(value));
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
value, sizeof(value));
if (ret >= 0) {
val = atoi(value);
if (val > 0)
select_devices(adev, hfpmod.ucid);
}
}
memset(value, 0, sizeof(value));
ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_VOL_MIXER_CTL,
value, sizeof(value));
if (ret >= 0) {
ALOGD("%s: mixer ctl name: %s", __func__, value);
strcpy(hfpmod.hfp_vol_mixer_ctl, value);
str_parms_del(parms, AUDIO_PARAMETER_HFP_VOL_MIXER_CTL);
}
memset(value, 0, sizeof(value));
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME,
value, sizeof(value));
if (ret >= 0) {
if (sscanf(value, "%f", &vol) != 1){
ALOGE("%s: error in retrieving hfp volume", __func__);
ret = -EIO;
goto exit;
}
ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
hfp_set_volume(adev, vol);
}
memset(value, 0, sizeof(value));
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_MIC_VOLUME,
value, sizeof(value));
if (ret >= 0) {
if (sscanf(value, "%f", &vol) != 1){
ALOGE("%s: error in retrieving hfp mic volume", __func__);
ret = -EIO;
goto exit;
}
ALOGD("%s: set_hfp_mic_volume usecase, Vol: [%f]", __func__, vol);
hfp_set_mic_volume(adev, vol);
}
exit:
ALOGV("%s Exit",__func__);
}

View file

@ -0,0 +1,147 @@
/*
* Copyright (C) 2015 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 "hardware_cal"
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
#ifdef HWDEP_CAL_ENABLED
#include <stdlib.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <cutils/log.h>
#include <audio_hw.h>
#include "audio_extn.h"
#include "sound/msmcal-hwdep.h"
#define SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID (100)
#define MAX_CAL_NAME 20
typedef struct acdb_audio_cal_cfg {
uint32_t persist;
uint32_t snd_dev_id;
audio_devices_t dev_id;
int32_t acdb_dev_id;
uint32_t app_type;
uint32_t topo_id;
uint32_t sampling_rate;
uint32_t cal_type;
uint32_t module_id;
uint32_t param_id;
} acdb_audio_cal_cfg_t;
struct param_data {
int use_case;
int acdb_id;
int get_size;
int buff_size;
int data_size;
void *buff;
};
char cal_name_info[WCD9XXX_MAX_CAL][MAX_CAL_NAME] = {
[WCD9XXX_ANC_CAL] = "anc_cal",
[WCD9XXX_MBHC_CAL] = "mbhc_cal",
[WCD9XXX_MAD_CAL] = "mad_cal",
};
typedef int (*acdb_get_calibration_t)(char *attr, int size, void *data);
acdb_get_calibration_t acdb_get_calibration;
static int hw_util_open(int card_no)
{
int fd = -1;
char dev_name[256];
snprintf(dev_name, sizeof(dev_name), "/dev/snd/hwC%uD%u",
card_no, WCD9XXX_CODEC_HWDEP_NODE);
ALOGD("%s: Opening device %s\n", __func__, dev_name);
fd = open(dev_name, O_WRONLY);
if (fd < 0) {
ALOGE("%s: cannot open device '%s'\n", __func__, dev_name);
return fd;
}
ALOGD("%s: success", __func__);
return fd;
}
static int send_codec_cal(acdb_get_calibration_t acdb_loader_get_calibration, int fd)
{
int ret = 0, type;
for (type = WCD9XXX_ANC_CAL; type < WCD9XXX_MAX_CAL; type++) {
struct wcdcal_ioctl_buffer codec_buffer;
struct param_data calib;
if (!strcmp(cal_name_info[type], "mad_cal"))
calib.acdb_id = SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID;
calib.get_size = 1;
ret = acdb_loader_get_calibration(cal_name_info[type], sizeof(struct param_data),
&calib);
if (ret < 0) {
ALOGE("%s get_calibration failed\n", __func__);
return ret;
}
calib.get_size = 0;
calib.buff = malloc(calib.buff_size);
ret = acdb_loader_get_calibration(cal_name_info[type],
sizeof(struct param_data), &calib);
if (ret < 0) {
ALOGE("%s get_calibration failed\n", __func__);
free(calib.buff);
return ret;
}
codec_buffer.buffer = calib.buff;
codec_buffer.size = calib.data_size;
codec_buffer.cal_type = type;
if (ioctl(fd, SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE, &codec_buffer) < 0)
ALOGE("Failed to call ioctl for %s err=%d",
cal_name_info[type], errno);
ALOGD("%s cal sent for %s", __func__, cal_name_info[type]);
free(calib.buff);
}
return ret;
}
void audio_extn_hwdep_cal_send(int snd_card, void *acdb_handle)
{
int fd;
fd = hw_util_open(snd_card);
if (fd == -1) {
ALOGE("%s error open\n", __func__);
return;
}
acdb_get_calibration = (acdb_get_calibration_t)
dlsym(acdb_handle, "acdb_loader_get_calibration");
if (acdb_get_calibration == NULL) {
ALOGE("%s: ERROR. dlsym Error:%s acdb_loader_get_calibration", __func__,
dlerror());
return;
}
if (send_codec_cal(acdb_get_calibration, fd) < 0)
ALOGE("%s: Could not send anc cal", __FUNCTION__);
close(fd);
}
#endif

View file

@ -0,0 +1,670 @@
/*
* Copyright (C) 2016 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_sndmonitor"
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
/* monitor sound card, cpe state
audio_dev registers for a callback from this module in adev_open
Each stream in audio_hal registers for a callback in
adev_open_*_stream.
A thread is spawned to poll() on sound card state files in /proc.
On observing a sound card state change, this thread invokes the
callbacks registered.
Callbacks are deregistered in adev_close_*_stream and adev_close
*/
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <pthread.h>
#include <cutils/list.h>
#include <cutils/hashmap.h>
#include <cutils/log.h>
#include <cutils/str_parms.h>
#include <ctype.h>
#include "audio_hw.h"
#include "audio_extn.h"
//#define MONITOR_DEVICE_EVENTS
#define CPE_MAGIC_NUM 0x2000
#define MAX_CPE_SLEEP_RETRY 2
#define CPE_SLEEP_WAIT 100
#define MAX_SLEEP_RETRY 100
#define AUDIO_INIT_SLEEP_WAIT 100 /* 100 ms */
#define AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE "ext_audio_device"
typedef enum {
audio_event_on,
audio_event_off
} audio_event_status;
typedef struct {
int card;
int fd;
struct listnode node; // membership in sndcards list
card_status_t status;
} sndcard_t;
typedef struct {
char * dev;
int fd;
int status;
struct listnode node; // membership in deviceevents list;
} dev_event_t;
typedef void (* notifyfn)(const void * target, const char * msg);
typedef struct {
const void * target;
notifyfn notify;
struct listnode cards;
unsigned int num_cards;
struct listnode dev_events;
unsigned int num_dev_events;
pthread_t monitor_thread;
int intpipe[2];
Hashmap * listeners; // from stream * -> callback func
bool initcheck;
} sndmonitor_state_t;
static sndmonitor_state_t sndmonitor;
static char * read_state(int fd)
{
struct stat buf;
if (fstat(fd, &buf) < 0)
return NULL;
off_t pos = lseek(fd, 0, SEEK_CUR);
off_t avail = buf.st_size - pos;
if (avail <= 0) {
ALOGD("avail %ld", avail);
return NULL;
}
char * state = (char *)calloc(avail+1, sizeof(char));
if (!state)
return NULL;
ssize_t bytes=read(fd, state, avail);
if (bytes <= 0)
return NULL;
// trim trailing whitespace
while (bytes && isspace(*(state+bytes-1))) {
*(state + bytes - 1) = '\0';
--bytes;
}
lseek(fd, 0, SEEK_SET);
return state;
}
static int add_new_sndcard(int card, int fd)
{
sndcard_t * s = (sndcard_t *)calloc(sizeof(sndcard_t), 1);
if (!s)
return -1;
s->card = card;
s->fd = fd; // dup?
char * state = read_state(fd);
bool online = state && !strcmp(state, "ONLINE");
ALOGV("card %d initial state %s %d", card, state, online);
if (state)
free(state);
s->status = online ? CARD_STATUS_ONLINE : CARD_STATUS_OFFLINE;
list_add_tail(&sndmonitor.cards, &s->node);
return 0;
}
static int enum_sndcards()
{
const char* cards = "/proc/asound/cards";
int tries = 10;
char *line = NULL;
size_t len = 0;
ssize_t bytes_read;
char path[128] = {0};
char *ptr, *saveptr, *card_id;
int line_no=0;
unsigned int num_cards=0, num_cpe=0;
FILE *fp;
int fd, ret;
while (--tries) {
if ((fp = fopen(cards, "r")) == NULL) {
ALOGE("Cannot open %s file to get list of sound cards", cards);
usleep(100000);
continue;
}
break;
}
if (!tries)
return -ENODEV;
while ((bytes_read = getline(&line, &len, fp) != -1)) {
// skip every other line to to match
// the output format of /proc/asound/cards
if (line_no++ % 2)
continue;
ptr = strtok_r(line, " [", &saveptr);
if (!ptr)
continue;
card_id = strtok_r(saveptr+1, "]", &saveptr);
if (!card_id)
continue;
// Limit to sound cards associated with ADSP
if ((strncasecmp(card_id, "msm", 3) != 0) &&
(strncasecmp(card_id, "sdm", 3) != 0) &&
(strncasecmp(card_id, "sdc", 3) != 0) &&
(strncasecmp(card_id, "apq", 3) != 0)) {
ALOGW("Skip over non-ADSP snd card %s", card_id);
continue;
}
snprintf(path, sizeof(path), "/proc/asound/card%s/state", ptr);
ALOGV("Opening sound card state : %s", path);
fd = open(path, O_RDONLY);
if (fd == -1) {
ALOGE("Open %s failed : %s", path, strerror(errno));
continue;
}
ret = add_new_sndcard(atoi(ptr), fd);
if (ret != 0) {
close(fd); // card state fd ownership is taken by sndcard on success
continue;
}
num_cards++;
// query cpe state for this card as well
tries=MAX_CPE_SLEEP_RETRY;
snprintf(path, sizeof(path), "/proc/asound/card%s/cpe0_state", ptr);
if (access(path, R_OK) < 0) {
ALOGW("access %s failed w/ err %s", path, strerror(errno));
continue;
}
ALOGV("Open cpe state card state %s", path);
while (--tries) {
if ((fd = open(path, O_RDONLY)) < 0) {
ALOGW("Open cpe state card state failed, retry : %s", path);
usleep(CPE_SLEEP_WAIT*1000);
continue;
}
break;
}
if (!tries)
continue;
ret = add_new_sndcard(CPE_MAGIC_NUM+num_cpe, fd);
if (ret != 0) {
close(fd); // card state fd ownership is taken by sndcard on success
continue;
}
num_cpe++;
num_cards++;
}
if (line)
free(line);
fclose(fp);
ALOGV("sndmonitor registerer num_cards %d", num_cards);
sndmonitor.num_cards = num_cards;
return num_cards ? 0 : -1;
}
static void free_sndcards()
{
while (!list_empty(&sndmonitor.cards)) {
struct listnode * n = list_head(&sndmonitor.cards);
sndcard_t * s = node_to_item(n, sndcard_t, node);
list_remove(n);
close(s->fd);
free(s);
}
}
static int add_new_dev_event(char * d_name, int fd)
{
dev_event_t * d = (dev_event_t *)calloc(sizeof(dev_event_t), 1);
if (!d)
return -1;
d->dev = strdup(d_name);
d->fd = fd;
list_add_tail(&sndmonitor.dev_events, &d->node);
return 0;
}
static int enum_dev_events()
{
const char* events_dir = "/sys/class/switch/";
DIR *dp;
struct dirent* in_file;
int fd;
char path[128] = {0};
unsigned int num_dev_events = 0;
if ((dp = opendir(events_dir)) == NULL) {
ALOGE("Cannot open switch directory %s err %s",
events_dir, strerror(errno));
return -1;
}
while ((in_file = readdir(dp)) != NULL) {
if (!strstr(in_file->d_name, "qc_"))
continue;
snprintf(path, sizeof(path), "%s/%s/state",
events_dir, in_file->d_name);
ALOGV("Opening audio dev event state : %s ", path);
fd = open(path, O_RDONLY);
if (fd == -1) {
ALOGE("Open %s failed : %s", path, strerror(errno));
} else {
if (!add_new_dev_event(in_file->d_name, fd))
num_dev_events++;
}
}
closedir(dp);
sndmonitor.num_dev_events = num_dev_events;
return num_dev_events ? 0 : -1;
}
static void free_dev_events()
{
while (!list_empty(&sndmonitor.dev_events)) {
struct listnode * n = list_head(&sndmonitor.dev_events);
dev_event_t * d = node_to_item(n, dev_event_t, node);
list_remove(n);
close(d->fd);
free(d->dev);
free(d);
}
}
static int notify(const struct str_parms * params)
{
if (!params)
return -1;
char * str = str_parms_to_str((struct str_parms *)params);
if (!str)
return -1;
if (sndmonitor.notify)
sndmonitor.notify(sndmonitor.target, str);
ALOGV("%s", str);
free(str);
return 0;
}
int on_dev_event(dev_event_t * dev_event)
{
char state_buf[2];
if (read(dev_event->fd, state_buf, 1) <= 0)
return -1;
lseek(dev_event->fd, 0, SEEK_SET);
state_buf[1]='\0';
if (atoi(state_buf) == dev_event->status)
return 0;
dev_event->status = atoi(state_buf);
struct str_parms * params = str_parms_create();
if (!params)
return -1;
char val[32] = {0};
snprintf(val, sizeof(val), "%s,%s", dev_event->dev,
dev_event->status ? "ON" : "OFF");
if (str_parms_add_str(params, AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE, val) < 0)
return -1;
int ret = notify(params);
str_parms_destroy(params);
return ret;
}
bool on_sndcard_state_update(sndcard_t * s)
{
char rd_buf[9]={0};
card_status_t status;
if (read(s->fd, rd_buf, 8) <= 0)
return -1;
rd_buf[8] = '\0';
lseek(s->fd, 0, SEEK_SET);
ALOGV("card num %d, new state %s", s->card, rd_buf);
bool is_cpe = (s->card >= CPE_MAGIC_NUM);
if (strstr(rd_buf, "OFFLINE"))
status = CARD_STATUS_OFFLINE;
else if (strstr(rd_buf, "ONLINE"))
status = CARD_STATUS_ONLINE;
else {
ALOGE("unknown state");
return 0;
}
if (status == s->status) // no change
return 0;
s->status = status;
struct str_parms * params = str_parms_create();
if (!params)
return -1;
char val[32] = {0};
// cpe actual card num is (card - MAGIC_NUM). so subtract accordingly
snprintf(val, sizeof(val), "%d,%s", s->card - (is_cpe ? CPE_MAGIC_NUM : 0),
status == CARD_STATUS_ONLINE ? "ONLINE" : "OFFLINE");
if (str_parms_add_str(params, is_cpe ? "CPE_STATUS" : "SND_CARD_STATUS",
val) < 0)
return -1;
int ret = notify(params);
str_parms_destroy(params);
return ret;
}
void * monitor_thread_loop(void * args __unused)
{
ALOGV("Start threadLoop()");
unsigned int num_poll_fds = sndmonitor.num_cards +
sndmonitor.num_dev_events + 1/*pipe*/;
struct pollfd * pfd = (struct pollfd *)calloc(sizeof(struct pollfd),
num_poll_fds);
if (!pfd)
return NULL;
pfd[0].fd = sndmonitor.intpipe[0];
pfd[0].events = POLLPRI|POLLIN;
int i=1;
struct listnode *node;
list_for_each(node, &sndmonitor.cards) {
sndcard_t * s = node_to_item(node, sndcard_t, node);
pfd[i].fd = s->fd;
pfd[i].events = POLLPRI;
++i;
}
list_for_each(node, &sndmonitor.dev_events) {
dev_event_t * d = node_to_item(node, dev_event_t, node);
pfd[i].fd = d->fd;
pfd[i].events = POLLPRI;
++i;
}
while (1) {
if (poll(pfd, num_poll_fds, -1) < 0) {
int errno_ = errno;
ALOGE("poll() failed w/ err %s", strerror(errno));
switch (errno_) {
case EINTR:
case ENOMEM:
sleep(2);
continue;
default:
/* above errors can be caused due to current system
state .. any other error is not expected */
LOG_ALWAYS_FATAL("unxpected poll() system call failure");
break;
}
}
ALOGV("out of poll()");
#define READY_TO_READ(p) ((p)->revents & (POLLIN|POLLPRI))
#define ERROR_IN_FD(p) ((p)->revents & (POLLERR|POLLHUP|POLLNVAL))
// check if requested to exit
if (READY_TO_READ(&pfd[0])) {
char buf[2]={0};
read(pfd[0].fd, buf, 1);
if (!strcmp(buf, "Q"))
break;
} else if (ERROR_IN_FD(&pfd[0])) {
// do not consider for poll again
// POLLERR - can this happen?
// POLLHUP - adev must not close pipe
// POLLNVAL - fd is valid
LOG_ALWAYS_FATAL("unxpected error in pipe poll fd 0x%x",
pfd[0].revents);
pfd[0].fd *= -1;
}
i=1;
list_for_each(node, &sndmonitor.cards) {
sndcard_t * s = node_to_item(node, sndcard_t, node);
if (READY_TO_READ(&pfd[i]))
on_sndcard_state_update(s);
else if (ERROR_IN_FD(&pfd[i])) {
// do not consider for poll again
// POLLERR - can this happen as we are reading from a fs?
// POLLHUP - not valid for cardN/state
// POLLNVAL - fd is valid
LOG_ALWAYS_FATAL("unxpected error in card poll fd 0x%x",
pfd[i].revents);
pfd[i].fd *= -1;
}
++i;
}
list_for_each(node, &sndmonitor.dev_events) {
dev_event_t * d = node_to_item(node, dev_event_t, node);
if (READY_TO_READ(&pfd[i]))
on_dev_event(d);
else if (ERROR_IN_FD(&pfd[i])) {
// do not consider for poll again
// POLLERR - can this happen as we are reading from a fs?
// POLLHUP - not valid for switch/state
// POLLNVAL - fd is valid
LOG_ALWAYS_FATAL("unxpected error in dev poll fd 0x%x",
pfd[i].revents);
pfd[i].fd *= -1;
}
++i;
}
}
return NULL;
}
// ---- listener static APIs ---- //
static int hashfn(void * key)
{
return (int)key;
}
static bool hasheq(void * key1, void *key2)
{
return key1 == key2;
}
static bool snd_cb(void* key, void* value, void* context)
{
snd_mon_cb cb = (snd_mon_cb)value;
cb(key, context);
return true;
}
static void snd_mon_update(const void * target __unused, const char * msg)
{
// target can be used to check if this message is intended for the
// recipient or not. (using some statically saved state)
struct str_parms *parms = str_parms_create_str(msg);
if (!parms)
return;
hashmapLock(sndmonitor.listeners);
hashmapForEach(sndmonitor.listeners, snd_cb, parms);
hashmapUnlock(sndmonitor.listeners);
str_parms_destroy(parms);
}
static int listeners_init()
{
sndmonitor.listeners = hashmapCreate(5, hashfn, hasheq);
if (!sndmonitor.listeners)
return -1;
return 0;
}
static int listeners_deinit()
{
// XXX TBD
return -1;
}
static int add_listener(void *stream, snd_mon_cb cb)
{
Hashmap * map = sndmonitor.listeners;
hashmapLock(map);
hashmapPut(map, stream, cb);
hashmapUnlock(map);
return 0;
}
static int del_listener(void * stream)
{
Hashmap * map = sndmonitor.listeners;
hashmapLock(map);
hashmapRemove(map, stream);
hashmapUnlock(map);
return 0;
}
// --- public APIs --- //
int audio_extn_snd_mon_deinit()
{
if (!sndmonitor.initcheck)
return -1;
write(sndmonitor.intpipe[1], "Q", 1);
pthread_join(sndmonitor.monitor_thread, (void **) NULL);
free_dev_events();
listeners_deinit();
free_sndcards();
close(sndmonitor.intpipe[0]);
close(sndmonitor.intpipe[1]);
sndmonitor.initcheck = 0;
return 0;
}
int audio_extn_snd_mon_init()
{
sndmonitor.notify = snd_mon_update;
sndmonitor.target = NULL; // unused for now
list_init(&sndmonitor.cards);
list_init(&sndmonitor.dev_events);
sndmonitor.initcheck = false;
if (pipe(sndmonitor.intpipe) < 0)
goto pipe_error;
if (enum_sndcards() < 0)
goto enum_sncards_error;
if (listeners_init() < 0)
goto listeners_error;
#ifdef MONITOR_DEVICE_EVENTS
enum_dev_events(); // failure here isn't fatal
#endif
int ret = pthread_create(&sndmonitor.monitor_thread,
(const pthread_attr_t *) NULL,
monitor_thread_loop, NULL);
if (ret) {
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)
{
if (!sndmonitor.initcheck) {
ALOGW("sndmonitor initcheck failed, cannot register");
return -1;
}
return add_listener(stream, cb);
}
int audio_extn_snd_mon_unregister_listener(void * stream)
{
if (!sndmonitor.initcheck) {
ALOGW("sndmonitor initcheck failed, cannot deregister");
return -1;
}
ALOGV("deregister listener for stream %p ", stream);
return del_listener(stream);
}

View file

@ -0,0 +1,369 @@
/*
* Copyright (C) 2015 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 "soundtrigger"
/* #define LOG_NDEBUG 0 */
#define LOG_NDDEBUG 0
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <pthread.h>
#include <unistd.h>
#include <cutils/log.h>
#include "audio_hw.h"
#include "audio_extn.h"
#include "platform.h"
#include "platform_api.h"
#include "sound_trigger_prop_intf.h"
#define XSTR(x) STR(x)
#define STR(x) #x
#ifdef __LP64__
#define SOUND_TRIGGER_LIBRARY_PATH "/system/vendor/lib64/hw/sound_trigger.primary.%s.so"
#else
#define SOUND_TRIGGER_LIBRARY_PATH "/system/vendor/lib/hw/sound_trigger.primary.%s.so"
#endif
struct sound_trigger_info {
struct sound_trigger_session_info st_ses;
bool lab_stopped;
struct listnode list;
};
struct sound_trigger_audio_device {
void *lib_handle;
struct audio_device *adev;
sound_trigger_hw_call_back_t st_callback;
struct listnode st_ses_list;
pthread_mutex_t lock;
};
static struct sound_trigger_audio_device *st_dev;
static struct sound_trigger_info *
get_sound_trigger_info(int capture_handle)
{
struct sound_trigger_info *st_ses_info = NULL;
struct listnode *node;
ALOGV("%s: list %d capture_handle %d", __func__,
list_empty(&st_dev->st_ses_list), capture_handle);
list_for_each(node, &st_dev->st_ses_list) {
st_ses_info = node_to_item(node, struct sound_trigger_info , list);
if (st_ses_info->st_ses.capture_handle == capture_handle)
return st_ses_info;
}
return NULL;
}
static void stdev_snd_mon_cb(void * stream __unused, struct str_parms * parms)
{
if (!parms)
return;
audio_extn_sound_trigger_set_parameters(NULL, parms);
return;
}
int audio_hw_call_back(sound_trigger_event_type_t event,
sound_trigger_event_info_t* config)
{
int status = 0;
struct sound_trigger_info *st_ses_info;
if (!st_dev)
return -EINVAL;
pthread_mutex_lock(&st_dev->lock);
switch (event) {
case ST_EVENT_SESSION_REGISTER:
if (!config) {
ALOGE("%s: NULL config", __func__);
status = -EINVAL;
break;
}
st_ses_info= calloc(1, sizeof(struct sound_trigger_info ));
if (!st_ses_info) {
ALOGE("%s: st_ses_info alloc failed", __func__);
status = -ENOMEM;
break;
}
memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses));
ALOGV("%s: add capture_handle %d pcm %p", __func__,
st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
list_add_tail(&st_dev->st_ses_list, &st_ses_info->list);
break;
case ST_EVENT_SESSION_DEREGISTER:
if (!config) {
ALOGE("%s: NULL config", __func__);
status = -EINVAL;
break;
}
st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle);
if (!st_ses_info) {
ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm);
status = -EINVAL;
break;
}
ALOGV("%s: remove capture_handle %d pcm %p", __func__,
st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
list_remove(&st_ses_info->list);
free(st_ses_info);
break;
default:
ALOGW("%s: Unknown event %d", __func__, event);
break;
}
pthread_mutex_unlock(&st_dev->lock);
return status;
}
int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
size_t bytes)
{
int ret = -1;
struct sound_trigger_info *st_info = NULL;
audio_event_info_t event;
if (!st_dev)
return ret;
if (!in->is_st_session_active) {
ALOGE(" %s: Sound trigger is not active", __func__);
goto exit;
}
if (in->standby)
in->standby = false;
pthread_mutex_lock(&st_dev->lock);
st_info = get_sound_trigger_info(in->capture_handle);
pthread_mutex_unlock(&st_dev->lock);
if (st_info) {
event.u.aud_info.ses_info = &st_info->st_ses;
event.u.aud_info.buf = buffer;
event.u.aud_info.num_bytes = bytes;
ret = st_dev->st_callback(AUDIO_EVENT_READ_SAMPLES, &event);
}
exit:
if (ret) {
if (-ENETRESET == ret)
in->is_st_session_active = false;
memset(buffer, 0, bytes);
ALOGV("%s: read failed status %d - sleep", __func__, ret);
usleep((bytes * 1000000) / (audio_stream_in_frame_size((struct audio_stream_in *)in) *
in->config.rate));
}
return ret;
}
void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
{
int status = 0;
struct sound_trigger_info *st_ses_info = NULL;
audio_event_info_t event;
if (!st_dev || !in)
return;
pthread_mutex_lock(&st_dev->lock);
st_ses_info = get_sound_trigger_info(in->capture_handle);
pthread_mutex_unlock(&st_dev->lock);
if (st_ses_info) {
event.u.ses_info = st_ses_info->st_ses;
ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
}
}
void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
{
struct sound_trigger_info *st_ses_info = NULL;
struct listnode *node;
if (!st_dev || !in)
return;
pthread_mutex_lock(&st_dev->lock);
in->is_st_session = false;
ALOGV("%s: list %d capture_handle %d", __func__,
list_empty(&st_dev->st_ses_list), in->capture_handle);
list_for_each(node, &st_dev->st_ses_list) {
st_ses_info = node_to_item(node, struct sound_trigger_info , list);
if (st_ses_info->st_ses.capture_handle == in->capture_handle) {
in->pcm = st_ses_info->st_ses.pcm;
in->config = st_ses_info->st_ses.config;
in->channel_mask = audio_channel_in_mask_from_count(in->config.channels);
in->is_st_session = true;
in->is_st_session_active = true;
ALOGV("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
break;
}
}
pthread_mutex_unlock(&st_dev->lock);
}
void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
st_event_type_t event)
{
int device_type = -1;
if (!st_dev)
return;
if (snd_device >= SND_DEVICE_OUT_BEGIN &&
snd_device < SND_DEVICE_OUT_END) {
device_type = PCM_PLAYBACK;
} else if (snd_device >= SND_DEVICE_IN_BEGIN &&
snd_device < SND_DEVICE_IN_END) {
if (snd_device == SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)
return;
device_type = PCM_CAPTURE;
} else {
ALOGE("%s: invalid device 0x%x, for event %d",
__func__, snd_device, event);
return;
}
ALOGV("%s: device 0x%x of type %d for Event %d",
__func__, snd_device, device_type, event);
if (device_type == PCM_CAPTURE) {
switch(event) {
case ST_EVENT_SND_DEVICE_FREE:
st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
break;
case ST_EVENT_SND_DEVICE_BUSY:
st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
break;
default:
ALOGW("%s:invalid event %d for device 0x%x",
__func__, event, snd_device);
}
}/*Events for output device, if required can be placed here in else*/
}
void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
struct str_parms *params)
{
audio_event_info_t event;
char value[32];
int ret, val;
if(!st_dev || !params) {
ALOGE("%s: str_params NULL", __func__);
return;
}
ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
sizeof(value));
if (ret > 0) {
if (strstr(value, "OFFLINE")) {
event.u.status = SND_CARD_STATUS_OFFLINE;
st_dev->st_callback(AUDIO_EVENT_SSR, &event);
}
else if (strstr(value, "ONLINE")) {
event.u.status = SND_CARD_STATUS_ONLINE;
st_dev->st_callback(AUDIO_EVENT_SSR, &event);
}
else
ALOGE("%s: unknown snd_card_status", __func__);
}
ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
if (ret > 0) {
if (strstr(value, "OFFLINE")) {
event.u.status = CPE_STATUS_OFFLINE;
st_dev->st_callback(AUDIO_EVENT_SSR, &event);
}
else if (strstr(value, "ONLINE")) {
event.u.status = CPE_STATUS_ONLINE;
st_dev->st_callback(AUDIO_EVENT_SSR, &event);
}
else
ALOGE("%s: unknown CPE status", __func__);
}
ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val);
if (ret >= 0) {
event.u.value = val;
st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event);
}
}
int audio_extn_sound_trigger_init(struct audio_device *adev)
{
int status = 0;
char sound_trigger_lib[100];
void *lib_handle;
ALOGV("%s: Enter", __func__);
st_dev = (struct sound_trigger_audio_device*)
calloc(1, sizeof(struct sound_trigger_audio_device));
if (!st_dev) {
ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
return -ENOMEM;
}
snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
SOUND_TRIGGER_LIBRARY_PATH,
XSTR(SOUND_TRIGGER_PLATFORM_NAME));
st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
if (st_dev->lib_handle == NULL) {
ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
dlerror());
status = -EINVAL;
goto cleanup;
}
ALOGV("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
st_dev->st_callback = (sound_trigger_hw_call_back_t)
dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");
if (st_dev->st_callback == NULL) {
ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
dlerror());
goto cleanup;
}
st_dev->adev = adev;
list_init(&st_dev->st_ses_list);
audio_extn_snd_mon_register_listener(st_dev, stdev_snd_mon_cb);
return 0;
cleanup:
if (st_dev->lib_handle)
dlclose(st_dev->lib_handle);
free(st_dev);
st_dev = NULL;
return status;
}
void audio_extn_sound_trigger_deinit(struct audio_device *adev)
{
ALOGV("%s: Enter", __func__);
if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
audio_extn_snd_mon_unregister_listener(st_dev);
dlclose(st_dev->lib_handle);
free(st_dev);
st_dev = NULL;
}
}

View file

@ -0,0 +1,907 @@
/*
* Copyright (C) 2015 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_spkr_prot"
/*#define LOG_NDEBUG 0*/
//#define LOG_NDDEBUG 0
#include <errno.h>
#include <math.h>
#include <cutils/log.h>
#include <fcntl.h>
#include "audio_hw.h"
#include "platform.h"
#include "platform_api.h"
#include <sys/stat.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <math.h>
#include <cutils/properties.h>
#include "audio_extn.h"
#include <linux/msm_audio_calibration.h>
#define THERMAL_CLIENT_LIBRARY_PATH "libthermalclient.so"
#ifdef SPKR_PROT_ENABLED
/*Range of spkr temparatures -30C to 80C*/
#define MIN_SPKR_TEMP_Q6 (-30 * (1 << 6))
#define MAX_SPKR_TEMP_Q6 (80 * (1 << 6))
#define VI_FEED_CHANNEL "VI_FEED_TX Channels"
/*Set safe temp value to 40C*/
#define SAFE_SPKR_TEMP 40
#define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6))
/*Range of resistance values 2ohms to 40 ohms*/
#define MIN_RESISTANCE_SPKR_Q24 (2 * (1 << 24))
#define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24))
/*Path where the calibration file will be stored*/
#define CALIB_FILE "/data/misc/audio/audio.cal"
/*Time between retries for calibartion or intial wait time
after boot up*/
#define WAIT_TIME_SPKR_CALIB (60 * 1000 * 1000)
#define MIN_SPKR_IDLE_SEC (60 * 30)
/*Once calibration is started sleep for 1 sec to allow
the calibration to kick off*/
#define SLEEP_AFTER_CALIB_START (3000)
/*If calibration is in progress wait for 200 msec before querying
for status again*/
#define WAIT_FOR_GET_CALIB_STATUS (200)
#define GET_SPKR_PROT_CAL_TIMEOUT_MSEC (5000)
/*Speaker states*/
#define SPKR_NOT_CALIBRATED -1
#define SPKR_CALIBRATED 1
/*Speaker processing state*/
#define SPKR_PROCESSING_IN_PROGRESS 1
#define SPKR_PROCESSING_IN_IDLE 0
/*Modes of Speaker Protection*/
enum speaker_protection_mode {
SPKR_PROTECTION_DISABLED = -1,
SPKR_PROTECTION_MODE_PROCESSING = 0,
SPKR_PROTECTION_MODE_CALIBRATE = 1,
};
struct speaker_prot_session {
int spkr_prot_mode;
int spkr_processing_state;
int thermal_client_handle;
pthread_mutex_t mutex_spkr_prot;
pthread_t spkr_calibration_thread;
pthread_mutex_t spkr_prot_thermalsync_mutex;
pthread_cond_t spkr_prot_thermalsync;
int cancel_spkr_calib;
pthread_cond_t spkr_calib_cancel;
pthread_mutex_t spkr_calib_cancelack_mutex;
pthread_cond_t spkr_calibcancel_ack;
pthread_t speaker_prot_threadid;
void *thermal_handle;
void *adev_handle;
int spkr_prot_t0;
struct pcm *pcm_rx;
struct pcm *pcm_tx;
int (*thermal_client_register_callback)
(char *client_name, int (*callback)(int), void *data);
void (*thermal_client_unregister_callback)(int handle);
int (*thermal_client_request)(char *client_name, int req_data);
bool spkr_prot_enable;
bool spkr_in_use;
struct timespec spkr_last_time_used;
};
static struct pcm_config pcm_config_skr_prot = {
.channels = 4,
.rate = 48000,
.period_size = 256,
.period_count = 4,
.format = PCM_FORMAT_S16_LE,
.start_threshold = 0,
.stop_threshold = INT_MAX,
.avail_min = 0,
};
static struct speaker_prot_session handle;
static int vi_feed_no_channels;
static void spkr_prot_set_spkrstatus(bool enable)
{
struct timespec ts;
if (enable)
handle.spkr_in_use = true;
else {
handle.spkr_in_use = false;
clock_gettime(CLOCK_BOOTTIME, &handle.spkr_last_time_used);
}
}
void audio_extn_spkr_prot_calib_cancel(void *adev)
{
pthread_t threadid;
struct audio_usecase *uc_info;
int count = 0;
threadid = pthread_self();
ALOGV("%s: Entry", __func__);
if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) {
ALOGV("%s: Calibration not in progress.. nothihg to cancel", __func__);
return;
}
uc_info = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_RX);
if (uc_info) {
pthread_mutex_lock(&handle.mutex_spkr_prot);
pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
handle.cancel_spkr_calib = 1;
pthread_cond_signal(&handle.spkr_calib_cancel);
pthread_mutex_unlock(&handle.mutex_spkr_prot);
pthread_cond_wait(&handle.spkr_calibcancel_ack,
&handle.spkr_calib_cancelack_mutex);
pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
}
ALOGV("%s: Exit", __func__);
}
static bool is_speaker_in_use(unsigned long *sec)
{
struct timespec temp;
if (!sec) {
ALOGE("%s: Invalid params", __func__);
return true;
}
if (handle.spkr_in_use) {
*sec = 0;
return true;
} else {
clock_gettime(CLOCK_BOOTTIME, &temp);
*sec = temp.tv_sec - handle.spkr_last_time_used.tv_sec;
return false;
}
}
static int get_spkr_prot_cal(int cal_fd,
struct audio_cal_info_msm_spk_prot_status *status)
{
int ret = 0;
struct audio_cal_fb_spk_prot_status cal_data;
if (cal_fd < 0) {
ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
ret = -EINVAL;
goto done;
}
if (status == NULL) {
ALOGE("%s: Error: status NULL", __func__);
ret = -EINVAL;
goto done;
}
cal_data.hdr.data_size = sizeof(cal_data);
cal_data.hdr.version = VERSION_0_0;
cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
cal_data.cal_type.cal_hdr.version = VERSION_0_0;
cal_data.cal_type.cal_hdr.buffer_number = 0;
cal_data.cal_type.cal_data.mem_handle = -1;
if (ioctl(cal_fd, AUDIO_GET_CALIBRATION, &cal_data)) {
ALOGE("%s: Error: AUDIO_GET_CALIBRATION failed!",
__func__);
ret = -ENODEV;
goto done;
}
status->r0[SP_V2_SPKR_1] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1];
status->r0[SP_V2_SPKR_2] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2];
status->status = cal_data.cal_type.cal_info.status;
done:
return ret;
}
static int set_spkr_prot_cal(int cal_fd,
struct audio_cal_info_spk_prot_cfg *protCfg)
{
int ret = 0;
struct audio_cal_fb_spk_prot_cfg cal_data;
char value[PROPERTY_VALUE_MAX];
if (cal_fd < 0) {
ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
ret = -EINVAL;
goto done;
}
if (protCfg == NULL) {
ALOGE("%s: Error: status NULL", __func__);
ret = -EINVAL;
goto done;
}
memset(&cal_data, 0, sizeof(cal_data));
cal_data.hdr.data_size = sizeof(cal_data);
cal_data.hdr.version = VERSION_0_0;
cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
cal_data.cal_type.cal_hdr.version = VERSION_0_0;
cal_data.cal_type.cal_hdr.buffer_number = 0;
cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1] = protCfg->r0[SP_V2_SPKR_1];
cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2] = protCfg->r0[SP_V2_SPKR_2];
cal_data.cal_type.cal_info.t0[SP_V2_SPKR_1] = protCfg->t0[SP_V2_SPKR_1];
cal_data.cal_type.cal_info.t0[SP_V2_SPKR_2] = protCfg->t0[SP_V2_SPKR_2];
cal_data.cal_type.cal_info.mode = protCfg->mode;
property_get("persist.spkr.cal.duration", value, "0");
if (atoi(value) > 0) {
ALOGD("%s: quick calibration enabled", __func__);
cal_data.cal_type.cal_info.quick_calib_flag = 1;
} else {
ALOGD("%s: quick calibration disabled", __func__);
cal_data.cal_type.cal_info.quick_calib_flag = 0;
}
cal_data.cal_type.cal_data.mem_handle = -1;
if (ioctl(cal_fd, AUDIO_SET_CALIBRATION, &cal_data)) {
ALOGE("%s: Error: AUDIO_SET_CALIBRATION failed!",
__func__);
ret = -ENODEV;
goto done;
}
done:
return ret;
}
static int vi_feed_get_channels(struct audio_device *adev)
{
struct mixer_ctl *ctl;
const char *mixer_ctl_name = VI_FEED_CHANNEL;
int value;
ALOGV("%s: entry", __func__);
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
if (!ctl) {
ALOGE("%s: Could not get ctl for mixer cmd - %s",
__func__, mixer_ctl_name);
goto error;
}
value = mixer_ctl_get_value(ctl, 0);
if (value < 0)
goto error;
else
return value+1;
error:
return -EINVAL;
}
// must be called with adev->lock acquired
static int spkr_calibrate(int t0)
{
struct audio_device *adev = handle.adev_handle;
struct audio_cal_info_spk_prot_cfg protCfg;
struct audio_cal_info_msm_spk_prot_status status;
bool cleanup = false, disable_rx = false, disable_tx = false;
int acdb_fd = -1;
struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL;
int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1;
struct timespec ts;
int retry_duration;
if (!adev) {
ALOGE("%s: Invalid params", __func__);
return -EINVAL;
}
if (!list_empty(&adev->usecase_list)) {
ALOGD("%s: Usecase present retry speaker protection", __func__);
return -EAGAIN;
}
acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
if (acdb_fd < 0) {
ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__);
return -ENODEV;
} else {
protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS;
/* HAL for speaker protection gets only one Temperature */
protCfg.t0[SP_V2_SPKR_1] = t0;
protCfg.t0[SP_V2_SPKR_2] = t0;
if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT",
__func__);
status.status = -ENODEV;
goto exit;
}
}
uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
if (!uc_info_rx) {
return -ENOMEM;
}
uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX;
uc_info_rx->type = PCM_PLAYBACK;
uc_info_rx->in_snd_device = SND_DEVICE_NONE;
uc_info_rx->stream.out = adev->primary_output;
uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED;
disable_rx = true;
list_add_tail(&adev->usecase_list, &uc_info_rx->list);
enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
enable_audio_route(adev, uc_info_rx);
pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK);
ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id);
if (pcm_dev_rx_id < 0) {
ALOGE("%s: Invalid pcm device for usecase (%d)",
__func__, uc_info_rx->id);
status.status = -ENODEV;
goto exit;
}
handle.pcm_rx = handle.pcm_tx = NULL;
handle.pcm_rx = pcm_open(adev->snd_card,
pcm_dev_rx_id,
PCM_OUT, &pcm_config_skr_prot);
if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) {
ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx));
status.status = -EIO;
goto exit;
}
uc_info_tx = (struct audio_usecase *)
calloc(1, sizeof(struct audio_usecase));
if (!uc_info_tx) {
status.status = -ENOMEM;
goto exit;
}
uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
uc_info_tx->type = PCM_CAPTURE;
uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
uc_info_tx->out_snd_device = SND_DEVICE_NONE;
disable_tx = true;
list_add_tail(&adev->usecase_list, &uc_info_tx->list);
enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
enable_audio_route(adev, uc_info_tx);
pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
if (pcm_dev_tx_id < 0) {
ALOGE("%s: Invalid pcm device for usecase (%d)",
__func__, uc_info_tx->id);
status.status = -ENODEV;
goto exit;
}
handle.pcm_tx = pcm_open(adev->snd_card,
pcm_dev_tx_id,
PCM_IN, &pcm_config_skr_prot);
if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
status.status = -EIO;
goto exit;
}
if (pcm_start(handle.pcm_rx) < 0) {
ALOGE("%s: pcm start for RX failed", __func__);
status.status = -EINVAL;
goto exit;
}
if (pcm_start(handle.pcm_tx) < 0) {
ALOGE("%s: pcm start for TX failed", __func__);
status.status = -EINVAL;
goto exit;
}
cleanup = true;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000);
ts.tv_nsec = 0;
pthread_mutex_lock(&handle.mutex_spkr_prot);
pthread_mutex_unlock(&adev->lock);
(void)pthread_cond_timedwait(&handle.spkr_calib_cancel,
&handle.mutex_spkr_prot, &ts);
ALOGD("%s: Speaker calibration done", __func__);
pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
if (handle.cancel_spkr_calib) {
status.status = -EAGAIN;
goto exit;
}
if (acdb_fd >= 0) {
status.status = -EINVAL;
retry_duration = 0;
while (!get_spkr_prot_cal(acdb_fd, &status) &&
retry_duration < GET_SPKR_PROT_CAL_TIMEOUT_MSEC) {
if (!status.status) {
ALOGD("%s: spkr_prot_thread calib Success R0 %d %d",
__func__, status.r0[SP_V2_SPKR_1], status.r0[SP_V2_SPKR_2]);
FILE *fp;
vi_feed_no_channels = vi_feed_get_channels(adev);
ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
if (vi_feed_no_channels < 0) {
ALOGE("%s: no of channels negative !!", __func__);
/* limit the number of channels to 2*/
vi_feed_no_channels = 2;
}
fp = fopen(CALIB_FILE,"wb");
if (!fp) {
ALOGE("%s: spkr_prot_thread File open failed %s",
__func__, strerror(errno));
status.status = -ENODEV;
} else {
int i;
/* HAL for speaker protection is always calibrating for stereo usecase*/
for (i = 0; i < vi_feed_no_channels; i++) {
fwrite(&status.r0[i], sizeof(status.r0[i]), 1, fp);
fwrite(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
}
fclose(fp);
}
break;
} else if (status.status == -EAGAIN) {
ALOGD("%s: spkr_prot_thread try again", __func__);
usleep(WAIT_FOR_GET_CALIB_STATUS * 1000);
retry_duration += WAIT_FOR_GET_CALIB_STATUS;
} else {
ALOGE("%s: spkr_prot_thread get failed status %d",
__func__, status.status);
break;
}
}
}
exit:
if (handle.pcm_rx)
pcm_close(handle.pcm_rx);
handle.pcm_rx = NULL;
if (handle.pcm_tx)
pcm_close(handle.pcm_tx);
handle.pcm_tx = NULL;
/* Clear TX calibration to handset mic */
platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
if (!status.status) {
protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1];
protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2];
if (set_spkr_prot_cal(acdb_fd, &protCfg))
ALOGE("%s: spkr_prot_thread disable calib mode", __func__);
else
handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
} else {
protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
if (set_spkr_prot_cal(acdb_fd, &protCfg))
ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__);
}
if (acdb_fd >= 0)
close(acdb_fd);
if (!handle.cancel_spkr_calib && cleanup) {
pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
pthread_cond_wait(&handle.spkr_calib_cancel, &handle.mutex_spkr_prot);
pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
}
if (disable_rx) {
list_remove(&uc_info_rx->list);
disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
disable_audio_route(adev, uc_info_rx);
}
if (disable_tx) {
list_remove(&uc_info_tx->list);
disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
disable_audio_route(adev, uc_info_tx);
}
if (uc_info_rx) free(uc_info_rx);
if (uc_info_tx) free(uc_info_tx);
if (cleanup) {
if (handle.cancel_spkr_calib)
pthread_cond_signal(&handle.spkr_calibcancel_ack);
handle.cancel_spkr_calib = 0;
pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
pthread_mutex_unlock(&handle.mutex_spkr_prot);
pthread_mutex_lock(&adev->lock);
}
return status.status;
}
static void* spkr_calibration_thread()
{
unsigned long sec = 0;
int t0;
bool goahead = false;
struct audio_cal_info_spk_prot_cfg protCfg;
FILE *fp;
int acdb_fd;
struct audio_device *adev = handle.adev_handle;
unsigned long min_idle_time = MIN_SPKR_IDLE_SEC;
char value[PROPERTY_VALUE_MAX];
/* If the value of this persist.spkr.cal.duration is 0
* then it means it will take 30min to calibrate
* and if the value is greater than zero then it would take
* that much amount of time to calibrate.
*/
property_get("persist.spkr.cal.duration", value, "0");
if (atoi(value) > 0)
min_idle_time = atoi(value);
handle.speaker_prot_threadid = pthread_self();
ALOGD("spkr_prot_thread enable prot Entry");
acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
if (acdb_fd >= 0) {
/*Set processing mode with t0/r0*/
protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
ALOGE("%s: spkr_prot_thread enable prot failed", __func__);
handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
close(acdb_fd);
} else
handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
} else {
handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
ALOGE("%s: Failed to open acdb node", __func__);
}
if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) {
ALOGD("%s: Speaker protection disabled", __func__);
pthread_exit(0);
return NULL;
}
fp = fopen(CALIB_FILE,"rb");
if (fp) {
int i;
bool spkr_calibrated = true;
/* HAL for speaker protection is always calibrating for stereo usecase*/
vi_feed_no_channels = vi_feed_get_channels(adev);
ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
if (vi_feed_no_channels < 0) {
ALOGE("%s: no of channels negative !!", __func__);
/* limit the number of channels to 2*/
vi_feed_no_channels = 2;
}
for (i = 0; i < vi_feed_no_channels; i++) {
fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp);
fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
}
ALOGD("%s: spkr_prot_thread r0 value %d %d",
__func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]);
ALOGD("%s: spkr_prot_thread t0 value %d %d",
__func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]);
fclose(fp);
/*Valid tempature range: -30C to 80C(in q6 format)
Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/
for (i = 0; i < vi_feed_no_channels; i++) {
if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6)
&& (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24)
&& (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) {
spkr_calibrated = false;
break;
}
}
if (spkr_calibrated) {
ALOGD("%s: Spkr calibrated", __func__);
protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
ALOGE("%s: enable prot failed", __func__);
handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
} else
handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
close(acdb_fd);
pthread_exit(0);
return NULL;
}
close(acdb_fd);
}
while (1) {
ALOGV("%s: start calibration", __func__);
if (!handle.thermal_client_request("spkr",1)) {
ALOGD("%s: wait for callback from thermal daemon", __func__);
pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
pthread_cond_wait(&handle.spkr_prot_thermalsync,
&handle.spkr_prot_thermalsync_mutex);
/*Convert temp into q6 format*/
t0 = (handle.spkr_prot_t0 * (1 << 6));
pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) {
ALOGE("%s: Calibration temparature error %d", __func__,
handle.spkr_prot_t0);
continue;
}
ALOGD("%s: Request t0 success value %d", __func__,
handle.spkr_prot_t0);
} else {
ALOGE("%s: Request t0 failed", __func__);
/*Assume safe value for temparature*/
t0 = SAFE_SPKR_TEMP_Q6;
}
goahead = false;
pthread_mutex_lock(&adev->lock);
if (is_speaker_in_use(&sec)) {
ALOGD("%s: Speaker in use retry calibration", __func__);
pthread_mutex_unlock(&adev->lock);
continue;
} else {
ALOGD("%s: speaker idle %ld min time %ld", __func__, sec, min_idle_time);
if (sec < min_idle_time) {
ALOGD("%s: speaker idle is less retry", __func__);
pthread_mutex_unlock(&adev->lock);
continue;
}
goahead = true;
}
if (!list_empty(&adev->usecase_list)) {
ALOGD("%s: Usecase active re-try calibration", __func__);
goahead = false;
pthread_mutex_unlock(&adev->lock);
}
if (goahead) {
int status;
status = spkr_calibrate(t0);
pthread_mutex_unlock(&adev->lock);
if (status == -EAGAIN) {
ALOGE("%s: failed to calibrate try again %s",
__func__, strerror(status));
continue;
} else {
ALOGE("%s: calibrate status %s", __func__, strerror(status));
}
ALOGD("%s: spkr_prot_thread end calibration", __func__);
break;
}
}
if (handle.thermal_client_handle)
handle.thermal_client_unregister_callback(handle.thermal_client_handle);
handle.thermal_client_handle = 0;
if (handle.thermal_handle)
dlclose(handle.thermal_handle);
handle.thermal_handle = NULL;
pthread_exit(0);
return NULL;
}
static int thermal_client_callback(int temp)
{
pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp);
if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED)
handle.spkr_prot_t0 = temp;
pthread_cond_signal(&handle.spkr_prot_thermalsync);
pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
return 0;
}
void audio_extn_spkr_prot_init(void *adev)
{
char value[PROPERTY_VALUE_MAX];
ALOGD("%s: Initialize speaker protection module", __func__);
memset(&handle, 0, sizeof(handle));
if (!adev) {
ALOGE("%s: Invalid params", __func__);
return;
}
property_get("persist.speaker.prot.enable", value, "");
handle.spkr_prot_enable = false;
if (!strncmp("true", value, 4))
handle.spkr_prot_enable = true;
if (!handle.spkr_prot_enable) {
ALOGD("%s: Speaker protection disabled", __func__);
return;
}
handle.adev_handle = adev;
handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
handle.spkr_prot_t0 = -1;
pthread_cond_init(&handle.spkr_prot_thermalsync, NULL);
pthread_cond_init(&handle.spkr_calib_cancel, NULL);
pthread_cond_init(&handle.spkr_calibcancel_ack, NULL);
pthread_mutex_init(&handle.mutex_spkr_prot, NULL);
pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL);
pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL);
handle.thermal_handle = dlopen(THERMAL_CLIENT_LIBRARY_PATH,
RTLD_NOW);
if (!handle.thermal_handle) {
ALOGE("%s: DLOPEN for thermal client failed", __func__);
} else {
/*Query callback function symbol*/
handle.thermal_client_register_callback =
(int (*)(char *, int (*)(int),void *))
dlsym(handle.thermal_handle, "thermal_client_register_callback");
handle.thermal_client_unregister_callback =
(void (*)(int) )
dlsym(handle.thermal_handle, "thermal_client_unregister_callback");
if (!handle.thermal_client_register_callback ||
!handle.thermal_client_unregister_callback) {
ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__);
} else {
/*Register callback function*/
handle.thermal_client_handle =
handle.thermal_client_register_callback("spkr", thermal_client_callback, NULL);
if (!handle.thermal_client_handle) {
ALOGE("%s: thermal_client_register_callback failed", __func__);
} else {
ALOGD("%s: spkr_prot thermal_client_register_callback success", __func__);
handle.thermal_client_request = (int (*)(char *, int))
dlsym(handle.thermal_handle, "thermal_client_request");
}
}
}
if (handle.thermal_client_request) {
ALOGD("%s: Create calibration thread", __func__);
(void)pthread_create(&handle.spkr_calibration_thread,
(const pthread_attr_t *) NULL, spkr_calibration_thread, &handle);
} else {
ALOGE("%s: thermal_client_request failed", __func__);
if (handle.thermal_client_handle &&
handle.thermal_client_unregister_callback)
handle.thermal_client_unregister_callback(handle.thermal_client_handle);
if (handle.thermal_handle)
dlclose(handle.thermal_handle);
handle.thermal_handle = NULL;
handle.spkr_prot_enable = false;
}
if (handle.spkr_prot_enable) {
char platform[PROPERTY_VALUE_MAX];
property_get("ro.board.platform", platform, "");
if (!strncmp("apq8084", platform, sizeof("apq8084"))) {
platform_set_snd_device_backend(SND_DEVICE_OUT_VOICE_SPEAKER,
"speaker-protected",
"SLIMBUS_0_RX");
}
}
}
int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device)
{
int acdb_id;
switch(snd_device) {
case SND_DEVICE_OUT_SPEAKER:
acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED);
break;
case SND_DEVICE_OUT_VOICE_SPEAKER:
acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED);
break;
default:
acdb_id = -EINVAL;
break;
}
return acdb_id;
}
int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)
{
if (!handle.spkr_prot_enable)
return snd_device;
switch(snd_device) {
case SND_DEVICE_OUT_SPEAKER:
return SND_DEVICE_OUT_SPEAKER_PROTECTED;
case SND_DEVICE_OUT_VOICE_SPEAKER:
return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
default:
return snd_device;
}
}
int audio_extn_spkr_prot_start_processing(snd_device_t snd_device)
{
struct audio_usecase *uc_info_tx;
struct audio_device *adev = handle.adev_handle;
int32_t pcm_dev_tx_id = -1, ret = 0;
ALOGV("%s: Entry", __func__);
if (!adev) {
ALOGE("%s: Invalid params", __func__);
return -EINVAL;
}
snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
spkr_prot_set_spkrstatus(true);
uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
if (!uc_info_tx) {
return -ENOMEM;
}
ALOGV("%s: snd_device(%d: %s)", __func__, snd_device,
platform_get_snd_device_name(snd_device));
audio_route_apply_and_update_path(adev->audio_route,
platform_get_snd_device_name(snd_device));
pthread_mutex_lock(&handle.mutex_spkr_prot);
if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) {
uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
uc_info_tx->type = PCM_CAPTURE;
uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
uc_info_tx->out_snd_device = SND_DEVICE_NONE;
handle.pcm_tx = NULL;
list_add_tail(&adev->usecase_list, &uc_info_tx->list);
enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
enable_audio_route(adev, uc_info_tx);
pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
if (pcm_dev_tx_id < 0) {
ALOGE("%s: Invalid pcm device for usecase (%d)",
__func__, uc_info_tx->id);
ret = -ENODEV;
goto exit;
}
handle.pcm_tx = pcm_open(adev->snd_card,
pcm_dev_tx_id,
PCM_IN, &pcm_config_skr_prot);
if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
ret = -EIO;
goto exit;
}
if (pcm_start(handle.pcm_tx) < 0) {
ALOGE("%s: pcm start for TX failed", __func__);
ret = -EINVAL;
}
}
exit:
/* Clear VI feedback cal and replace with handset MIC */
platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
if (ret) {
if (handle.pcm_tx)
pcm_close(handle.pcm_tx);
handle.pcm_tx = NULL;
list_remove(&uc_info_tx->list);
disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
disable_audio_route(adev, uc_info_tx);
free(uc_info_tx);
} else
handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS;
pthread_mutex_unlock(&handle.mutex_spkr_prot);
ALOGV("%s: Exit", __func__);
return ret;
}
void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)
{
struct audio_usecase *uc_info_tx;
struct audio_device *adev = handle.adev_handle;
ALOGV("%s: Entry", __func__);
snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
spkr_prot_set_spkrstatus(false);
pthread_mutex_lock(&handle.mutex_spkr_prot);
if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) {
uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX);
if (handle.pcm_tx)
pcm_close(handle.pcm_tx);
handle.pcm_tx = NULL;
disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
if (uc_info_tx) {
list_remove(&uc_info_tx->list);
disable_audio_route(adev, uc_info_tx);
free(uc_info_tx);
}
}
handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
pthread_mutex_unlock(&handle.mutex_spkr_prot);
if (adev)
audio_route_reset_and_update_path(adev->audio_route,
platform_get_snd_device_name(snd_device));
ALOGV("%s: Exit", __func__);
}
bool audio_extn_spkr_prot_is_enabled()
{
return handle.spkr_prot_enable;
}
#endif /*SPKR_PROT_ENABLED*/

View file

@ -0,0 +1,545 @@
/*
* Copyright (C) 2013-2016 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 "tfa_98xx"
/*#define LOG_NDEBUG 0*/
#include <cutils/log.h>
#include <stdlib.h>
#include <audio_hw.h>
#include <dlfcn.h>
#include "audio_extn.h"
#include <platform.h>
#include <math.h>
#define LIB_SPEAKER_BUNDLE "/system/lib/libexTfa98xx.so"
enum exTfa98xx_Audio_Mode
{
Audio_Mode_None = -1,
Audio_Mode_Music_Normal,
Audio_Mode_Hfp_Client,
Audio_Mode_Voice,
Audio_Mode_Hs_Hfp,
Audio_Mode_Max
};
typedef enum exTfa98xx_Audio_Mode exTfa98xx_audio_mode_t;
enum exTfa98xx_Func_Mode
{
Func_Mode_None = -1,
Func_Mode_Speaker,
Func_Mode_BT
};
typedef enum exTfa98xx_Func_Mode exTfa98xx_func_mode_t;
#define I2S_CLOCK_ENABLE 1
#define I2S_CLOCK_DISABLE 0
#define HFP_MAX_VOLUME (15.000000)
#define TFA_98XX_HFP_VSETPS (5.0)
exTfa98xx_audio_mode_t current_audio_mode = Audio_Mode_None;
typedef int (*set_speaker_on_t)(exTfa98xx_audio_mode_t);
typedef int (*set_speaker_off_t)(void);
typedef int (*set_speaker_calibration_t)(int);
typedef void (*set_speaker_volume_step_t)(int, int);
struct speaker_data {
struct audio_device *adev;
void *speaker_bundle;
set_speaker_on_t set_speaker_on;
set_speaker_off_t set_speaker_off;
set_speaker_calibration_t set_speaker_calibration;
set_speaker_volume_step_t set_speaker_volume_step;
int ref_cnt[Audio_Mode_Max];
int route_cnt[Audio_Mode_Max];
bool update_ref_cnt;
};
struct speaker_data *tfa98xx_speaker_data = NULL;
static struct speaker_data* open_speaker_bundle()
{
struct speaker_data *sd = calloc(1, sizeof(struct speaker_data));
sd->speaker_bundle = dlopen(LIB_SPEAKER_BUNDLE, RTLD_NOW);
if (sd->speaker_bundle == NULL) {
ALOGE("%s: DLOPEN failed for %s", __func__, LIB_SPEAKER_BUNDLE);
goto error;
} else {
ALOGV("%s: DLOPEN successful for %s", __func__, LIB_SPEAKER_BUNDLE);
sd->set_speaker_on = (set_speaker_on_t)dlsym(sd->speaker_bundle,
"exTfa98xx_speakeron");
if (sd->set_speaker_on == NULL) {
ALOGE("%s: dlsym error %s for exTfa98xx_speakeron", __func__,
dlerror());
goto error;
}
sd->set_speaker_off = (set_speaker_off_t)dlsym(sd->speaker_bundle,
"exTfa98xx_speakeroff");
if (sd->set_speaker_off == NULL) {
ALOGE("%s: dlsym error %s for exTfa98xx_speakeroff", __func__,
dlerror());
goto error;
}
sd->set_speaker_volume_step = (set_speaker_volume_step_t)dlsym(sd->speaker_bundle,
"exTfa98xx_setvolumestep");
if (sd->set_speaker_volume_step == NULL) {
ALOGE("%s: dlsym error %s for exTfa98xx_setvolumestep",
__func__, dlerror());
goto error;
}
sd->set_speaker_calibration = (set_speaker_calibration_t)dlsym(sd->speaker_bundle,
"exTfa98xx_calibration");
if (sd->set_speaker_calibration == NULL) {
ALOGE("%s: dlsym error %s for exTfa98xx_calibration",
__func__, dlerror());
goto error;
}
}
return sd;
error:
free(sd);
return 0;
}
static void close_speaker_bundle(struct speaker_data *sd)
{
if (sd != NULL) {
dlclose(sd->speaker_bundle);
free(sd);
sd = NULL;
}
}
static int adev_i2s_clock_operation(int enable, struct audio_device *adev, char *paths)
{
int ret = -1;
ALOGD("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable);
if(I2S_CLOCK_ENABLE == enable) {
ret = audio_route_apply_and_update_path(adev->audio_route, paths);
if(ret) {
ALOGE("%s: audio_route_apply_and_update_path return %d\n", __func__, ret);
return ret;
}
} else {
ret = audio_route_reset_and_update_path(adev->audio_route, paths);
if(ret) {
ALOGE("%s: audio_route_reset_and_update_path return %d\n", __func__, ret);
return ret;
}
}
return 0;
}
static int tfa_98xx_set_audio_mode(int enable, struct audio_device *adev, exTfa98xx_audio_mode_t audio_mode)
{
char paths[32] = "init_smart_pa";
switch(audio_mode) {
case Audio_Mode_Music_Normal:
strcat(paths, " music");
break;
case Audio_Mode_Voice:
case Audio_Mode_Hfp_Client:
case Audio_Mode_Hs_Hfp:
strcat(paths, " voice");
break;
default:
ALOGE("%s: function %d not support!\n",__func__, audio_mode);
return -EINVAL;
}
ALOGV("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable);
adev_i2s_clock_operation(enable, adev, paths);
return 0;
}
static exTfa98xx_audio_mode_t tfa_98xx_get_audio_mode(struct speaker_data *data)
{
exTfa98xx_audio_mode_t tfa_98xx_audio_mode = Audio_Mode_None;
struct listnode *node;
struct audio_usecase *usecase;
audio_mode_t mode = data->adev->mode;
int i = 0;
ALOGV("%s: enter\n", __func__);
for (i = 0; i < Audio_Mode_Max; i++)
data->route_cnt[i] = 0;
list_for_each(node, &data->adev->usecase_list) {
usecase = node_to_item(node, struct audio_usecase, list);
if (usecase->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
tfa_98xx_audio_mode = Audio_Mode_Hs_Hfp;
data->route_cnt[tfa_98xx_audio_mode]++;
ALOGV("%s: audio_mode hs_hfp\n", __func__);
}
} else if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) {
if ((mode == AUDIO_MODE_IN_CALL) || audio_extn_hfp_is_active(data->adev)) {
if (audio_extn_hfp_is_active(data->adev)) {
if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
tfa_98xx_audio_mode = Audio_Mode_Hfp_Client;
data->route_cnt[tfa_98xx_audio_mode]++;
ALOGV("%s: audio_mode hfp client\n", __func__);
}
} else {
if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
tfa_98xx_audio_mode = Audio_Mode_Voice;
data->route_cnt[tfa_98xx_audio_mode]++;
ALOGV("%s: audio_mode voice\n", __func__);
}
}
} else {
if (data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
tfa_98xx_audio_mode = Audio_Mode_Music_Normal;
data->route_cnt[tfa_98xx_audio_mode]++;
ALOGV("%s: tfa_98xx_audio_mode music\n", __func__);
}
}
} else {
ALOGE("%s: no device match \n", __func__);
}
}
ALOGV("%s: tfa_98xx_audio_mode %d exit\n", __func__, tfa_98xx_audio_mode);
return tfa_98xx_audio_mode;
}
static int tfa_98xx_set_func_mode(int enable, struct audio_device *adev, exTfa98xx_func_mode_t func_mode)
{
struct speaker_data *data = tfa98xx_speaker_data;
char paths[32] = "init_smart_pa";
if (data) {
switch(func_mode) {
case Func_Mode_Speaker:
strcat(paths, " func_speaker");
break;
case Func_Mode_BT:
strcat(paths, " func_bt");
break;
default:
ALOGE("%s: function %d not support!\n",__func__, func_mode);
return -EINVAL;
}
ALOGV("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable);
adev_i2s_clock_operation(enable, adev, paths);
}
return 0;
}
static exTfa98xx_func_mode_t tfa_98xx_get_func_mode(exTfa98xx_audio_mode_t audio_mode)
{
exTfa98xx_func_mode_t func_mode = Func_Mode_None;
switch(audio_mode) {
case Audio_Mode_Music_Normal:
case Audio_Mode_Voice:
ALOGV("%s: tfa_98xx_func_mode speaker \n", __func__);
func_mode = Func_Mode_Speaker;
break;
case Audio_Mode_Hfp_Client:
case Audio_Mode_Hs_Hfp:
ALOGV("%s: tfa_98xx_func_mode bt \n", __func__);
func_mode = Func_Mode_BT;
break;
default:
break;
}
return func_mode;
}
static void tfa_98xx_disable_speaker(void)
{
struct speaker_data *data = tfa98xx_speaker_data;
int ret = 0;
ret = data->set_speaker_off();
if (ret) {
ALOGE("%s: exTfa98xx_speakeroff failed result = %d\n", __func__, ret);
goto on_error;
}
ret = tfa_98xx_set_audio_mode(I2S_CLOCK_DISABLE, data->adev, current_audio_mode);
if (ret) {
ALOGE("%s: tfa_98xx_set_audio_mode disable failed return %d\n", __func__, ret);
goto on_error;
}
current_audio_mode = Audio_Mode_None;
on_error:
return;
}
void audio_extn_tfa_98xx_disable_speaker(snd_device_t snd_device)
{
struct speaker_data *data = tfa98xx_speaker_data;
int i = 0;
exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_None;
ALOGV("%s: enter\n", __func__);
if (data) {
if ((current_audio_mode == Audio_Mode_None) || (snd_device > SND_DEVICE_OUT_END))
goto on_exit;
switch(snd_device) {
case SND_DEVICE_OUT_SPEAKER:
new_audio_mode = Audio_Mode_Music_Normal;
break;
case SND_DEVICE_OUT_VOICE_SPEAKER:
new_audio_mode = Audio_Mode_Voice;
break;
case SND_DEVICE_OUT_VOICE_SPEAKER_HFP:
new_audio_mode = Audio_Mode_Hfp_Client;
break;
case SND_DEVICE_OUT_BT_SCO:
new_audio_mode = Audio_Mode_Hs_Hfp;
break;
default:
break;
}
if ((new_audio_mode == Audio_Mode_None) || (data->ref_cnt[new_audio_mode] <= 0)) {
ALOGE("%s: device ref cnt is already 0", __func__);
goto on_exit;
}
data->ref_cnt[new_audio_mode]--;
for (i = 0; i < Audio_Mode_Max; i++) {
if (data->ref_cnt[i] > 0) {
ALOGD("%s: exTfa98xx_speaker still in use\n", __func__);
goto on_exit;
}
}
if (data->adev->enable_hfp)
data->set_speaker_volume_step(0, 0);
tfa_98xx_disable_speaker();
}
ALOGV("%s: exit\n", __func__);
on_exit:
return;
}
int audio_extn_tfa_98xx_enable_speaker(void)
{
struct speaker_data *data = tfa98xx_speaker_data;
exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_Music_Normal;
int ret = 0;
int i = 0;
ALOGV("%s: enter\n", __func__);
if (data) {
new_audio_mode = tfa_98xx_get_audio_mode(data);
if ((new_audio_mode != Audio_Mode_None) && (data->ref_cnt[new_audio_mode] >= 1)) {
ALOGD("%s, mode %d already active!", __func__, new_audio_mode);
data->ref_cnt[new_audio_mode]++;
goto on_exit;
}
ret = tfa_98xx_set_audio_mode(I2S_CLOCK_ENABLE, data->adev, new_audio_mode);
if (ret) {
ALOGE("%s: tfa_98xx_set_audio_mode enable failed return %d\n", __func__, ret);
goto on_exit;
}
ret = data->set_speaker_on(new_audio_mode);
if (ret) {
ALOGE("%s: exTfa98xx_speakeron failed result = %d\n", __func__, ret);
goto on_exit;
}
current_audio_mode = new_audio_mode;
for (i = 0; i < Audio_Mode_Max; i++) {
data->ref_cnt[i] = data->route_cnt[i];
}
data->update_ref_cnt = false;
}
ALOGV("%s: exit\n", __func__);
on_exit:
return ret;
}
void audio_extn_tfa_98xx_set_mode(void)
{
int ret = 0;
struct speaker_data *data = tfa98xx_speaker_data;
exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_None;
exTfa98xx_func_mode_t new_func_mode = Func_Mode_None;
ALOGV("%s: enter\n", __func__);
if (data) {
new_audio_mode = tfa_98xx_get_audio_mode(data);
new_func_mode = tfa_98xx_get_func_mode(new_audio_mode);
if (new_func_mode == Func_Mode_None)
return;
ret = tfa_98xx_set_func_mode(I2S_CLOCK_ENABLE, data->adev, new_func_mode);
if (ret) {
ALOGE("%s: tfa_98xx_set_func_mode enable return %d\n", __func__, ret);
}
data->update_ref_cnt = true;
}
ALOGV("%s: exit\n", __func__);
}
void audio_extn_tfa_98xx_set_mode_bt(void)
{
struct speaker_data *data = tfa98xx_speaker_data;
int ret = 0;
if (data) {
ret = tfa_98xx_set_func_mode(I2S_CLOCK_ENABLE, data->adev, Func_Mode_BT);
if (ret) {
ALOGE("%s: tfa_98xx_set_func_mode enable return %d\n", __func__, ret);
}
}
}
void audio_extn_tfa_98xx_update(void)
{
struct speaker_data *data = tfa98xx_speaker_data;
exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_Music_Normal;
ALOGD("%s: enter\n", __func__);
if (data) {
new_audio_mode = tfa_98xx_get_audio_mode(data);
if (new_audio_mode <= current_audio_mode) {
ALOGE("%s: audio_extn_tfa_98xx_update same mode\n", __func__);
if (data->update_ref_cnt == true) {
data->ref_cnt[new_audio_mode]++;
data->update_ref_cnt = false;
}
goto on_error;
}
if (current_audio_mode != Audio_Mode_None) {
tfa_98xx_disable_speaker();
}
audio_extn_tfa_98xx_enable_speaker();
}
ALOGV("%s: exit\n", __func__);
on_error:
return;
}
void audio_extn_tfa_98xx_set_voice_vol(float vol)
{
struct speaker_data *data = tfa98xx_speaker_data;
int vsteps = 0;
if (data) {
if (data->adev->enable_hfp) {
if (vol < 0.0) {
vol = 0.0;
} else {
vol = ((vol > HFP_MAX_VOLUME) ? 1.0 : (vol / HFP_MAX_VOLUME));
}
vsteps = (int)floorf((1.0 - vol) * TFA_98XX_HFP_VSETPS);
} else {
return;
}
ALOGD("%s: vsteps %d\n", __func__, vsteps);
data->set_speaker_volume_step(vsteps, vsteps);
}
}
bool audio_extn_tfa_98xx_is_supported(void)
{
struct speaker_data *data = tfa98xx_speaker_data;
if (data)
return true;
else
return false;
}
int audio_extn_tfa_98xx_init(struct audio_device *adev)
{
int ret = 0;
struct speaker_data *data = open_speaker_bundle();
ALOGV("%s: enter\n", __func__);
if (data) {
ret = tfa_98xx_set_audio_mode(I2S_CLOCK_ENABLE, adev, Audio_Mode_Music_Normal);
if (ret) {
ALOGE("%s: tfa_98xx_set_audio_mode enable return %d\n", __func__, ret);
goto err_init;
}
ret = data->set_speaker_calibration(0);
if (ret) {
ALOGE("%s: exTfa98xx_calibration return %d\n", __func__, ret);
}
ret = tfa_98xx_set_audio_mode(I2S_CLOCK_DISABLE, adev, Audio_Mode_Music_Normal);
if (ret) {
ALOGE("%s: tfa_98xx_set_audio_mode disable return %d\n", __func__, ret);
goto err_init;
}
data->adev = adev;
tfa98xx_speaker_data = data;
ALOGV("%s: exit\n", __func__);
return 0;
}
err_init:
close_speaker_bundle(data);
return -EINVAL;
}
void audio_extn_tfa_98xx_deinit(void)
{
struct speaker_data *data = tfa98xx_speaker_data;
if (data) {
data->set_speaker_off();
close_speaker_bundle(data);
tfa98xx_speaker_data = NULL;
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2013-2016 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.
*/
#ifndef TFA_98XX_H
#define TFA_98XX_H
#ifdef SMART_PA_TFA_98XX_SUPPORTED
int audio_extn_tfa_98xx_enable_speaker(void);
void audio_extn_tfa_98xx_disable_speaker(snd_device_t snd_device);
void audio_extn_tfa_98xx_set_mode();
void audio_extn_tfa_98xx_set_mode_bt(void);
void audio_extn_tfa_98xx_update(void);
void audio_extn_tfa_98xx_set_voice_vol(float vol);
int audio_extn_tfa_98xx_init(struct audio_device *adev);
void audio_extn_tfa_98xx_deinit(void);
bool audio_extn_tfa_98xx_is_supported(void);
#else
#define audio_extn_tfa_98xx_enable_speaker(void) (0)
#define audio_extn_tfa_98xx_disable_speaker(snd_device) (0)
#define audio_extn_tfa_98xx_set_mode() (0)
#define audio_extn_tfa_98xx_set_mode_bt() (0)
#define audio_extn_tfa_98xx_update(void) (0)
#define audio_extn_tfa_98xx_set_voice_vol(vol) (0)
#define audio_extn_tfa_98xx_init(adev) (0)
#define audio_extn_tfa_98xx_deinit(void) (0)
#define audio_extn_tfa_98xx_is_supported(void) (false)
#endif
#endif /* TFA_98XX_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,608 @@
/*
* Copyright (C) 2016 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_utils"
//#define LOG_NDEBUG 0
#include <errno.h>
#include <cutils/properties.h>
#include <cutils/config_utils.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>
#include <cutils/str_parms.h>
#include <cutils/log.h>
#include <cutils/misc.h>
#include "acdb.h"
#include "audio_hw.h"
#include "platform.h"
#include "platform_api.h"
#include "audio_extn.h"
#define MAX_LENGTH_MIXER_CONTROL_IN_INT 128
static int set_stream_app_type_mixer_ctrl(struct audio_device *adev,
int pcm_device_id, int app_type,
int acdb_dev_id, int sample_rate,
int stream_type,
snd_device_t snd_device)
{
char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
struct mixer_ctl *ctl;
int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc = 0;
int snd_device_be_idx = -1;
if (stream_type == PCM_PLAYBACK) {
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
"Audio Stream %d App Type Cfg", pcm_device_id);
} else if (stream_type == PCM_CAPTURE) {
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
"Audio Stream Capture %d App Type Cfg", pcm_device_id);
}
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
if (!ctl) {
ALOGE("%s: Could not get ctl for mixer cmd - %s",
__func__, mixer_ctl_name);
rc = -EINVAL;
goto exit;
}
app_type_cfg[len++] = app_type;
app_type_cfg[len++] = acdb_dev_id;
app_type_cfg[len++] = sample_rate;
snd_device_be_idx = platform_get_snd_device_backend_index(snd_device);
if (snd_device_be_idx > 0)
app_type_cfg[len++] = snd_device_be_idx;
ALOGV("%s: stream type %d app_type %d, acdb_dev_id %d "
"sample rate %d, snd_device_be_idx %d",
__func__, stream_type, app_type, acdb_dev_id, sample_rate,
snd_device_be_idx);
mixer_ctl_set_array(ctl, app_type_cfg, len);
exit:
return rc;
}
void audio_extn_utils_send_default_app_type_cfg(void *platform, struct mixer *mixer)
{
int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {-1};
int length = 0, app_type = 0,rc = 0;
struct mixer_ctl *ctl = NULL;
const char *mixer_ctl_name = "App Type Config";
ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
if (!ctl) {
ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name);
return;
}
rc = platform_get_default_app_type_v2(platform, PCM_PLAYBACK, &app_type);
if (rc == 0) {
app_type_cfg[length++] = 1;
app_type_cfg[length++] = app_type;
app_type_cfg[length++] = 48000;
app_type_cfg[length++] = 16;
mixer_ctl_set_array(ctl, app_type_cfg, length);
}
return;
}
static const char *flags_to_mode(int dir, uint32_t flags)
{
if (dir == 0) {
if (flags & AUDIO_OUTPUT_FLAG_VOIP_RX) {
return "voip";
}
} else if (dir == 1) {
if (flags & AUDIO_INPUT_FLAG_VOIP_TX) {
return "voip";
}
}
return "default";
}
static int audio_extn_utils_send_app_type_cfg_hfp(struct audio_device *adev,
struct audio_usecase *usecase)
{
struct mixer_ctl *ctl;
int pcm_device_id, acdb_dev_id = 0, snd_device = usecase->out_snd_device;
int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
int app_type = 0, rc = 0;
ALOGV("%s", __func__);
if (usecase->type != PCM_HFP_CALL) {
ALOGV("%s: not a playback or HFP path, no need to cfg app type", __func__);
rc = 0;
goto exit_send_app_type_cfg;
}
if ((usecase->id != USECASE_AUDIO_HFP_SCO) &&
(usecase->id != USECASE_AUDIO_HFP_SCO_WB)) {
ALOGV("%s: a playback path where app type cfg is not required", __func__);
rc = 0;
goto exit_send_app_type_cfg;
}
snd_device = usecase->out_snd_device;
pcm_device_id = platform_get_pcm_device_id(usecase->id, PCM_PLAYBACK);
snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device;
acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
if (acdb_dev_id < 0) {
ALOGE("%s: Couldn't get the acdb dev id", __func__);
rc = -EINVAL;
goto exit_send_app_type_cfg;
}
if (usecase->type == PCM_HFP_CALL) {
/* config HFP session:1 playback path */
rc = platform_get_default_app_type_v2(adev->platform, PCM_PLAYBACK, &app_type);
if (rc < 0)
goto exit_send_app_type_cfg;
sample_rate= CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
acdb_dev_id, sample_rate,
PCM_PLAYBACK,
SND_DEVICE_NONE); // use legacy behavior
if (rc < 0)
goto exit_send_app_type_cfg;
/* config HFP session:1 capture path */
rc = platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
if (rc == 0) {
rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
acdb_dev_id, sample_rate,
PCM_CAPTURE,
SND_DEVICE_NONE);
if (rc < 0)
goto exit_send_app_type_cfg;
}
/* config HFP session:2 capture path */
pcm_device_id = HFP_ASM_RX_TX;
snd_device = usecase->in_snd_device;
acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
if (acdb_dev_id <= 0) {
ALOGE("%s: Couldn't get the acdb dev id", __func__);
rc = -EINVAL;
goto exit_send_app_type_cfg;
}
rc = platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
if (rc == 0) {
rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
acdb_dev_id, sample_rate, PCM_CAPTURE,
SND_DEVICE_NONE);
if (rc < 0)
goto exit_send_app_type_cfg;
}
/* config HFP session:2 playback path */
rc = platform_get_default_app_type_v2(adev->platform, PCM_PLAYBACK, &app_type);
if (rc == 0) {
rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
acdb_dev_id, sample_rate,
PCM_PLAYBACK, SND_DEVICE_NONE);
if (rc < 0)
goto exit_send_app_type_cfg;
}
}
rc = 0;
exit_send_app_type_cfg:
return rc;
}
static int derive_capture_app_type_cfg(struct audio_device *adev,
struct audio_usecase *usecase,
int *app_type,
int *sample_rate)
{
if (usecase->stream.in == NULL) {
return -1;
}
struct stream_in *in = usecase->stream.in;
struct stream_app_type_cfg *app_type_cfg = &in->app_type_cfg;
*sample_rate = DEFAULT_INPUT_SAMPLING_RATE;
if (audio_is_usb_in_device(in->device)) {
platform_check_and_update_copp_sample_rate(adev->platform,
usecase->in_snd_device,
in->sample_rate,
sample_rate);
}
app_type_cfg->mode = flags_to_mode(1 /*capture*/, in->flags);
ALOGV("%s mode %s", __func__, app_type_cfg->mode);
if (in->format == AUDIO_FORMAT_PCM_16_BIT) {
platform_get_app_type_v2(adev->platform,
PCM_CAPTURE,
app_type_cfg->mode,
16,
app_type_cfg->sample_rate,
app_type);
} else if (in->format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
in->format == AUDIO_FORMAT_PCM_8_24_BIT) {
platform_get_app_type_v2(adev->platform,
PCM_CAPTURE,
app_type_cfg->mode,
24,
app_type_cfg->sample_rate,
app_type);
} else if (in->format == AUDIO_FORMAT_PCM_32_BIT) {
platform_get_app_type_v2(adev->platform,
PCM_CAPTURE,
app_type_cfg->mode,
32,
app_type_cfg->sample_rate,
app_type);
} else {
ALOGE("%s bad format\n", __func__);
return -1;
}
app_type_cfg->app_type = *app_type;
app_type_cfg->sample_rate = *sample_rate;
return 0;
}
static int derive_playback_app_type_cfg(struct audio_device *adev,
struct audio_usecase *usecase,
int *app_type,
int *sample_rate)
{
if (usecase->stream.out == NULL) {
return -1;
}
struct stream_out *out = usecase->stream.out;
struct stream_app_type_cfg *app_type_cfg = &out->app_type_cfg;
*sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
// add speaker prot changes if needed
// and use that to check for device
if (audio_is_usb_out_device(out->devices)) {
platform_check_and_update_copp_sample_rate(adev->platform,
usecase->out_snd_device,
out->sample_rate,
sample_rate);
}
app_type_cfg->mode = flags_to_mode(0 /*playback*/, out->flags);
if (!audio_is_linear_pcm(out->format)) {
platform_get_app_type_v2(adev->platform,
PCM_PLAYBACK,
app_type_cfg->mode,
24,
*sample_rate,
app_type);
ALOGV("Non pcm got app type %d", *app_type);
} else if (out->format == AUDIO_FORMAT_PCM_16_BIT) {
platform_get_app_type_v2(adev->platform,
PCM_PLAYBACK,
app_type_cfg->mode,
16,
*sample_rate,
app_type);
} else if (out->format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
out->format == AUDIO_FORMAT_PCM_8_24_BIT) {
platform_get_app_type_v2(adev->platform,
PCM_PLAYBACK,
app_type_cfg->mode,
24,
*sample_rate,
app_type);
} else if (out->format == AUDIO_FORMAT_PCM_32_BIT) {
platform_get_app_type_v2(adev->platform,
PCM_PLAYBACK,
app_type_cfg->mode,
32,
*sample_rate,
app_type);
} else {
ALOGE("%s bad format\n", __func__);
return -1;
}
app_type_cfg->app_type = *app_type;
app_type_cfg->sample_rate = *sample_rate;
return 0;
}
static int derive_acdb_dev_id(struct audio_device *adev __unused,
struct audio_usecase *usecase)
{
struct stream_out *out;
struct stream_in *in;
if (usecase->type == PCM_PLAYBACK) {
return platform_get_snd_device_acdb_id(usecase->out_snd_device);
} else if(usecase->type == PCM_CAPTURE) {
return platform_get_snd_device_acdb_id(usecase->in_snd_device);
}
return -1;
}
int audio_extn_utils_send_app_type_cfg(struct audio_device *adev,
struct audio_usecase *usecase)
{
int len = 0;
int sample_rate;
int app_type;
int acdb_dev_id;
size_t app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {0};
int pcm_device_id;
struct mixer_ctl *ctl;
int ret;
if (usecase->type == PCM_HFP_CALL) {
return audio_extn_utils_send_app_type_cfg_hfp(adev, usecase);
}
if (!platform_supports_app_type_cfg())
return -1;
if (usecase->type == PCM_PLAYBACK) {
ret = derive_playback_app_type_cfg(adev,
usecase,
&app_type,
&sample_rate);
} else if (usecase->type == PCM_CAPTURE) {
ret = derive_capture_app_type_cfg(adev,
usecase,
&app_type,
&sample_rate);
} else {
ALOGE("%s: Invalid uc type : 0x%x", __func__, usecase->type);
return -1;
}
if (ret < 0) {
ALOGE("%s: Failed to derive app_type for uc type : 0x%x", __func__,
usecase->type);
return -1;
}
acdb_dev_id = derive_acdb_dev_id(adev, usecase);
if (acdb_dev_id <= 0) {
ALOGE("%s: Couldn't get the acdb dev id", __func__);
return -1;
}
pcm_device_id = platform_get_pcm_device_id(usecase->id, usecase->type);
set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type, acdb_dev_id,
sample_rate,
usecase->type,
usecase->type == PCM_PLAYBACK ? usecase->out_snd_device :
usecase->in_snd_device);
return 0;
}
int audio_extn_utils_send_app_type_gain(struct audio_device *adev,
int app_type,
int *gain)
{
int gain_cfg[4];
const char *mixer_ctl_name = "App Type Gain";
struct mixer_ctl *ctl;
ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
if (!ctl) {
ALOGE("%s: Could not get volume ctl mixer %s", __func__,
mixer_ctl_name);
return -EINVAL;
}
gain_cfg[0] = 0;
gain_cfg[1] = app_type;
gain_cfg[2] = gain[0];
gain_cfg[3] = gain[1];
ALOGV("%s app_type %d l(%d) r(%d)", __func__, app_type, gain[0], gain[1]);
return mixer_ctl_set_array(ctl, gain_cfg,
sizeof(gain_cfg)/sizeof(gain_cfg[0]));
}
// this assumes correct app_type and sample_rate fields
// have been set for the stream using audio_extn_utils_send_app_type_cfg
void audio_extn_utils_send_audio_calibration(struct audio_device *adev,
struct audio_usecase *usecase)
{
int type = usecase->type;
int app_type = 0;
if (type == PCM_PLAYBACK && usecase->stream.out != NULL) {
struct stream_out *out = usecase->stream.out;
ALOGV("%s send cal for app_type %d, rate %d", __func__,
out->app_type_cfg.app_type,
out->app_type_cfg.sample_rate);
platform_send_audio_calibration_v2(adev->platform, usecase,
out->app_type_cfg.app_type,
out->app_type_cfg.sample_rate);
} else if (type == PCM_CAPTURE && usecase->stream.in != NULL) {
struct stream_in *in = usecase->stream.in;
ALOGV("%s send cal for capture app_type %d, rate %d", __func__,
in->app_type_cfg.app_type,
in->app_type_cfg.sample_rate);
platform_send_audio_calibration_v2(adev->platform, usecase,
in->app_type_cfg.app_type,
in->app_type_cfg.sample_rate);
} else {
/* when app type is default. the sample rate is not used to send cal */
platform_get_default_app_type_v2(adev->platform, type, &app_type);
platform_send_audio_calibration_v2(adev->platform, usecase, app_type,
48000);
}
}
#define MAX_SND_CARD 8
#define RETRY_US 500000
#define RETRY_NUMBER 10
#define min(a, b) ((a) < (b) ? (a) : (b))
static const char *kConfigLocationList[] =
{"/odm/etc", "/vendor/etc", "/system/etc"};
static const int kConfigLocationListSize =
(sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
bool audio_extn_utils_resolve_config_file(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;
}
/* platform_info_file should be size 'MIXER_PATH_MAX_LENGTH' */
int audio_extn_utils_get_platform_info(const char* snd_card_name, char* platform_info_file)
{
if (NULL == snd_card_name) {
return -1;
}
struct snd_card_split *snd_split_handle = NULL;
int ret = 0;
audio_extn_set_snd_card_split(snd_card_name);
snd_split_handle = audio_extn_get_snd_card_split();
snprintf(platform_info_file, MIXER_PATH_MAX_LENGTH, "%s_%s_%s.xml",
PLATFORM_INFO_XML_BASE_STRING, snd_split_handle->snd_card,
snd_split_handle->form_factor);
if (!audio_extn_utils_resolve_config_file(platform_info_file)) {
memset(platform_info_file, 0, MIXER_PATH_MAX_LENGTH);
snprintf(platform_info_file, MIXER_PATH_MAX_LENGTH, "%s_%s.xml",
PLATFORM_INFO_XML_BASE_STRING, snd_split_handle->snd_card);
if (!audio_extn_utils_resolve_config_file(platform_info_file)) {
memset(platform_info_file, 0, MIXER_PATH_MAX_LENGTH);
strlcpy(platform_info_file, PLATFORM_INFO_XML_PATH, MIXER_PATH_MAX_LENGTH);
ret = audio_extn_utils_resolve_config_file(platform_info_file) ? 0 : -1;
}
}
return ret;
}
int audio_extn_utils_get_snd_card_num()
{
void *hw_info = NULL;
struct mixer *mixer = NULL;
int retry_num = 0;
int snd_card_num = 0;
const char* snd_card_name = NULL;
char platform_info_file[MIXER_PATH_MAX_LENGTH]= {0};
struct acdb_platform_data *my_data = calloc(1, sizeof(struct acdb_platform_data));
bool card_verifed[MAX_SND_CARD] = {0};
const int retry_limit = property_get_int32("audio.snd_card.open.retries", RETRY_NUMBER);
for (;;) {
if (snd_card_num >= MAX_SND_CARD) {
if (retry_num++ >= retry_limit) {
ALOGE("%s: Unable to find correct sound card, aborting.", __func__);
snd_card_num = -1;
goto done;
}
snd_card_num = 0;
usleep(RETRY_US);
continue;
}
if (card_verifed[snd_card_num]) {
++snd_card_num;
continue;
}
mixer = mixer_open(snd_card_num);
if (!mixer) {
ALOGE("%s: Unable to open the mixer card: %d", __func__,
snd_card_num);
++snd_card_num;
continue;
}
card_verifed[snd_card_num] = true;
snd_card_name = mixer_get_name(mixer);
hw_info = hw_info_init(snd_card_name);
if (audio_extn_utils_get_platform_info(snd_card_name, platform_info_file) < 0) {
ALOGE("Failed to find platform_info_file");
goto cleanup;
}
/* Initialize snd card name specific ids and/or backends*/
if (snd_card_info_init(platform_info_file, my_data,
&acdb_set_parameters) < 0) {
ALOGE("Failed to find platform_info_file");
goto cleanup;
}
/* validate the sound card name
* my_data->snd_card_name can contain
* <a> complete sound card name, i.e. <device>-<codec>-<form_factor>-snd-card
* example: msm8994-tomtom-mtp-snd-card
* <b> or sub string of the card name, i.e. <device>-<codec>
* example: msm8994-tomtom
* snd_card_name is truncated to 32 charaters as per mixer_get_name() implementation
* so use min of my_data->snd_card_name and snd_card_name length for comparison
*/
if (my_data->snd_card_name != NULL &&
strncmp(snd_card_name, my_data->snd_card_name,
min(strlen(snd_card_name), strlen(my_data->snd_card_name))) != 0) {
ALOGI("%s: found valid sound card %s, but not primary sound card %s",
__func__, snd_card_name, my_data->snd_card_name);
goto cleanup;
}
ALOGI("%s: found sound card %s, primary sound card expected is %s",
__func__, snd_card_name, my_data->snd_card_name);
break;
cleanup:
++snd_card_num;
mixer_close(mixer);
mixer = NULL;
hw_info_deinit(hw_info);
hw_info = NULL;
}
done:
mixer_close(mixer);
hw_info_deinit(hw_info);
if (my_data)
free(my_data);
return snd_card_num;
}