/* alsa_default.cpp ** ** Copyright 2009 Wind River Systems ** Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. ** ** 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 "ALSAModule" //#define LOG_NDEBUG 0 #define LOG_NDDEBUG 0 #include <utils/Log.h> #include <cutils/properties.h> #include <linux/ioctl.h> #include "AudioUtil.h" #include "AudioHardwareALSA.h" #include <media/AudioRecord.h> #include <dlfcn.h> #ifdef QCOM_CSDCLIENT_ENABLED extern "C" { static int (*csd_disable_device)(); static int (*csd_enable_device)(int, int, uint32_t); static int (*csd_volume)(int); static int (*csd_mic_mute)(int); static int (*csd_wide_voice)(uint8_t); static int (*csd_slow_talk)(uint8_t); static int (*csd_fens)(uint8_t); static int (*csd_start_voice)(); static int (*csd_stop_voice)(); } #endif #ifndef ALSA_DEFAULT_SAMPLE_RATE #define ALSA_DEFAULT_SAMPLE_RATE 44100 // in Hz #endif #define BTSCO_RATE_16KHZ 16000 #define USECASE_TYPE_RX 1 #define USECASE_TYPE_TX 2 #define MAX_HDMI_CHANNEL_CNT 6 namespace android_audio_legacy { static int s_device_open(const hw_module_t*, const char*, hw_device_t**); static int s_device_close(hw_device_t*); static status_t s_init(alsa_device_t *, ALSAHandleList &); static status_t s_open(alsa_handle_t *); static status_t s_close(alsa_handle_t *); static status_t s_standby(alsa_handle_t *); static status_t s_route(alsa_handle_t *, uint32_t, int); static status_t s_start_voice_call(alsa_handle_t *); static status_t s_start_voip_call(alsa_handle_t *); static status_t s_start_fm(alsa_handle_t *); static void s_set_voice_volume(int); static void s_set_voip_volume(int); static void s_set_mic_mute(int); static void s_set_voip_mic_mute(int); static void s_set_voip_config(int, int); static status_t s_set_fm_vol(int); static void s_set_btsco_rate(int); static status_t s_set_lpa_vol(int); static void s_enable_wide_voice(bool flag); static void s_enable_fens(bool flag); static void s_set_flags(uint32_t flags); static status_t s_set_compressed_vol(int); static void s_enable_slow_talk(bool flag); static void s_set_voc_rec_mode(uint8_t mode); static void s_set_volte_mic_mute(int state); static void s_set_volte_volume(int vol); static bool s_is_tmus(); #ifdef SEPERATED_AUDIO_INPUT static void s_setInput(int); static int input_source; #endif static int mccmnc; #ifdef QCOM_CSDCLIENT_ENABLED static void s_set_csd_handle(void*); #endif static char mic_type[25]; static char curRxUCMDevice[50]; static char curTxUCMDevice[50]; static int fluence_mode; static int fmVolume; #ifdef USES_FLUENCE_INCALL static uint32_t mDevSettingsFlag = TTY_OFF | DMIC_FLAG; #else static uint32_t mDevSettingsFlag = TTY_OFF; #endif static int btsco_samplerate = 8000; static ALSAUseCaseList mUseCaseList; static void *csd_handle; static hw_module_methods_t s_module_methods = { open : s_device_open }; extern "C" { hw_module_t HAL_MODULE_INFO_SYM = { tag : HARDWARE_MODULE_TAG, version_major : 1, version_minor : 0, id : ALSA_HARDWARE_MODULE_ID, name : "QCOM ALSA module", author : "QuIC Inc", methods : &s_module_methods, dso : 0, reserved : {0,}, }; } static int s_device_open(const hw_module_t* module, const char* name, hw_device_t** device) { char value[128]; alsa_device_t *dev; dev = (alsa_device_t *) malloc(sizeof(*dev)); if (!dev) return -ENOMEM; memset(dev, 0, sizeof(*dev)); /* initialize the procs */ dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = 0; dev->common.module = (hw_module_t *) module; dev->common.close = s_device_close; dev->init = s_init; dev->open = s_open; dev->close = s_close; dev->route = s_route; dev->standby = s_standby; dev->startVoiceCall = s_start_voice_call; dev->startVoipCall = s_start_voip_call; dev->startFm = s_start_fm; dev->setVoiceVolume = s_set_voice_volume; dev->setVoipVolume = s_set_voip_volume; dev->setMicMute = s_set_mic_mute; dev->setVoipMicMute = s_set_voip_mic_mute; dev->setVoipConfig = s_set_voip_config; dev->setFmVolume = s_set_fm_vol; dev->setBtscoRate = s_set_btsco_rate; dev->setLpaVolume = s_set_lpa_vol; dev->enableWideVoice = s_enable_wide_voice; dev->enableFENS = s_enable_fens; dev->setFlags = s_set_flags; dev->setCompressedVolume = s_set_compressed_vol; dev->enableSlowTalk = s_enable_slow_talk; dev->setVocRecMode = s_set_voc_rec_mode; dev->setVoLTEMicMute = s_set_volte_mic_mute; dev->setVoLTEVolume = s_set_volte_volume; #ifdef SEPERATED_AUDIO_INPUT dev->setInput = s_setInput; #endif #ifdef QCOM_CSDCLIENT_ENABLED dev->setCsdHandle = s_set_csd_handle; #endif *device = &dev->common; property_get("persist.audio.handset.mic",value,"0"); strlcpy(mic_type, value, sizeof(mic_type)); property_get("persist.audio.fluence.mode",value,"0"); if (!strcmp("broadside", value)) { fluence_mode = FLUENCE_MODE_BROADSIDE; } else { fluence_mode = FLUENCE_MODE_ENDFIRE; } strlcpy(curRxUCMDevice, "None", sizeof(curRxUCMDevice)); strlcpy(curTxUCMDevice, "None", sizeof(curTxUCMDevice)); ALOGV("ALSA module opened"); return 0; } static int s_device_close(hw_device_t* device) { free(device); device = NULL; return 0; } // ---------------------------------------------------------------------------- static const int DEFAULT_SAMPLE_RATE = ALSA_DEFAULT_SAMPLE_RATE; static void switchDevice(alsa_handle_t *handle, uint32_t devices, uint32_t mode); static char *getUCMDevice(uint32_t devices, int input, char *rxDevice); static void disableDevice(alsa_handle_t *handle); int getUseCaseType(const char *useCase); static int callMode = AudioSystem::MODE_NORMAL; // ---------------------------------------------------------------------------- bool platform_is_Fusion3() { char platform[128], baseband[128]; property_get("ro.board.platform", platform, ""); property_get("ro.baseband", baseband, ""); if (!strcmp("msm8960", platform) && !strcmp("mdm", baseband)) return true; else return false; } int deviceName(alsa_handle_t *handle, unsigned flags, char **value) { int ret = 0; char ident[70]; if (flags & PCM_IN) { strlcpy(ident, "CapturePCM/", sizeof(ident)); } else { strlcpy(ident, "PlaybackPCM/", sizeof(ident)); } strlcat(ident, handle->useCase, sizeof(ident)); ret = snd_use_case_get(handle->ucMgr, ident, (const char **)value); ALOGD("Device value returned is %s", (*value)); return ret; } status_t setHDMIChannelCount() { status_t err = NO_ERROR; int channel_count = 0; const char *channel_cnt_str = NULL; EDID_AUDIO_INFO info = { 0 }; ALSAControl control("/dev/snd/controlC0"); if (AudioUtil::getHDMIAudioSinkCaps(&info)) { for (int i = 0; i < info.nAudioBlocks && i < MAX_EDID_BLOCKS; i++) { if (info.AudioBlocksArray[i].nChannels > channel_count && info.AudioBlocksArray[i].nChannels <= MAX_HDMI_CHANNEL_CNT) { channel_count = info.AudioBlocksArray[i].nChannels; } } } switch (channel_count) { case 6: channel_cnt_str = "Six"; break; case 5: channel_cnt_str = "Five"; break; case 4: channel_cnt_str = "Four"; break; case 3: channel_cnt_str = "Three"; break; default: channel_cnt_str = "Two"; break; } ALOGD("HDMI channel count: %s", channel_cnt_str); control.set("HDMI_RX Channels", channel_cnt_str); return err; } status_t setHardwareParams(alsa_handle_t *handle) { struct snd_pcm_hw_params *params; unsigned long bufferSize, reqBuffSize; unsigned int periodTime, bufferTime; unsigned int requestedRate = handle->sampleRate; int status = 0; int channels = handle->channels; snd_pcm_format_t format = SNDRV_PCM_FORMAT_S16_LE; params = (snd_pcm_hw_params*) calloc(1, sizeof(struct snd_pcm_hw_params)); if (!params) { ALOGE("Failed to allocate ALSA hardware parameters!"); return NO_INIT; } reqBuffSize = handle->bufferSize; ALOGD("setHardwareParams: reqBuffSize %d channels %d sampleRate %d", (int) reqBuffSize, handle->channels, handle->sampleRate); #ifdef QCOM_SSR_ENABLED if (channels == 6) { if (!strncmp(handle->useCase, SND_USE_CASE_VERB_HIFI_REC, strlen(SND_USE_CASE_VERB_HIFI_REC)) || !strncmp(handle->useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, strlen(SND_USE_CASE_MOD_CAPTURE_MUSIC))) { ALOGV("HWParams: Use 4 channels in kernel for 5.1(%s) recording ", handle->useCase); channels = 4; } } #endif param_init(params); param_set_mask(params, SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (handle->format != SNDRV_PCM_FORMAT_S16_LE) { if (handle->format == AudioSystem::AMR_NB || handle->format == AudioSystem::AMR_WB #ifdef QCOM_QCHAT_ENABLED || handle->format == AudioSystem::EVRC || handle->format == AudioSystem::EVRCB || handle->format == AudioSystem::EVRCWB #endif ) format = SNDRV_PCM_FORMAT_SPECIAL; } param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, format); param_set_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT, SNDRV_PCM_SUBFORMAT_STD); param_set_int(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, reqBuffSize); param_set_int(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16); param_set_int(params, SNDRV_PCM_HW_PARAM_FRAME_BITS, channels * 16); param_set_int(params, SNDRV_PCM_HW_PARAM_CHANNELS, channels); param_set_int(params, SNDRV_PCM_HW_PARAM_RATE, handle->sampleRate); param_set_hw_refine(handle->handle, params); if (param_set_hw_params(handle->handle, params)) { ALOGE("cannot set hw params"); return NO_INIT; } param_dump(params); handle->handle->buffer_size = pcm_buffer_size(params); handle->handle->period_size = pcm_period_size(params); handle->handle->period_cnt = handle->handle->buffer_size/handle->handle->period_size; ALOGD("setHardwareParams: buffer_size %d, period_size %d, period_cnt %d", handle->handle->buffer_size, handle->handle->period_size, handle->handle->period_cnt); handle->handle->rate = handle->sampleRate; handle->handle->channels = handle->channels; handle->periodSize = handle->handle->period_size; if (strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_REC) && strcmp(handle->useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC) && (6 != handle->channels)) { //Do not update buffersize for 5.1 recording handle->bufferSize = handle->handle->period_size; } return NO_ERROR; } status_t setSoftwareParams(alsa_handle_t *handle) { struct snd_pcm_sw_params* params; struct pcm* pcm = handle->handle; unsigned long periodSize = pcm->period_size; int channels = handle->channels; params = (snd_pcm_sw_params*) calloc(1, sizeof(struct snd_pcm_sw_params)); if (!params) { LOG_ALWAYS_FATAL("Failed to allocate ALSA software parameters!"); return NO_INIT; } #ifdef QCOM_SSR_ENABLED if (channels == 6) { if (!strncmp(handle->useCase, SND_USE_CASE_VERB_HIFI_REC, strlen(SND_USE_CASE_VERB_HIFI_REC)) || !strncmp(handle->useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, strlen(SND_USE_CASE_MOD_CAPTURE_MUSIC))) { ALOGV("SWParams: Use 4 channels in kernel for 5.1(%s) recording ", handle->useCase); channels = 4; } } #endif // Get the current software parameters params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; params->period_step = 1; if(((!strcmp(handle->useCase,SND_USE_CASE_MOD_PLAY_VOIP)) || (!strcmp(handle->useCase,SND_USE_CASE_VERB_IP_VOICECALL)))){ ALOGV("setparam: start & stop threshold for Voip "); params->avail_min = handle->channels - 1 ? periodSize/4 : periodSize/2; params->start_threshold = periodSize/2; params->stop_threshold = INT_MAX; } else { params->avail_min = periodSize/(channels * 2); params->start_threshold = periodSize/(channels * 2); params->stop_threshold = INT_MAX; } params->silence_threshold = 0; params->silence_size = 0; if (param_set_sw_params(handle->handle, params)) { ALOGE("cannot set sw params"); return NO_INIT; } return NO_ERROR; } void switchDevice(alsa_handle_t *handle, uint32_t devices, uint32_t mode) { const char **mods_list; use_case_t useCaseNode; unsigned usecase_type = 0; bool inCallDevSwitch = false; char *rxDevice, *txDevice, ident[70], *use_case = NULL; int err = 0, index, mods_size; int rx_dev_id, tx_dev_id; ALOGD("%s: device %d mode:%d", __FUNCTION__, devices, mode); if ((mode == AudioSystem::MODE_IN_CALL) || (mode == AudioSystem::MODE_IN_COMMUNICATION)) { if ((devices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) || (devices & AudioSystem::DEVICE_IN_WIRED_HEADSET)) { devices = devices | (AudioSystem::DEVICE_OUT_WIRED_HEADSET | AudioSystem::DEVICE_IN_WIRED_HEADSET); } else if (devices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) { devices = devices | (AudioSystem::DEVICE_OUT_WIRED_HEADPHONE | AudioSystem::DEVICE_IN_BUILTIN_MIC); } else if (devices & AudioSystem::DEVICE_IN_BUILTIN_MIC) { if (mode == AudioSystem::MODE_IN_CALL) { devices |= AudioSystem::DEVICE_OUT_EARPIECE; } else if (mode == AudioSystem::MODE_IN_COMMUNICATION) { if (!strncmp(curRxUCMDevice, SND_USE_CASE_DEV_SPEAKER, MAX_LEN(curRxUCMDevice, SND_USE_CASE_DEV_SPEAKER))) { devices &= ~AudioSystem::DEVICE_IN_BUILTIN_MIC; devices |= AudioSystem::DEVICE_IN_BACK_MIC; } } } else if (devices & AudioSystem::DEVICE_OUT_EARPIECE) { devices = devices | AudioSystem::DEVICE_IN_BUILTIN_MIC; } else if (devices & AudioSystem::DEVICE_OUT_SPEAKER) { devices = devices | (AudioSystem::DEVICE_IN_BACK_MIC | AudioSystem::DEVICE_OUT_SPEAKER); } else if ((devices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO) || (devices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET) || (devices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET)) { devices = devices | (AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET | AudioSystem::DEVICE_OUT_BLUETOOTH_SCO); #ifdef QCOM_ANC_HEADSET_ENABLED } else if ((devices & AudioSystem::DEVICE_OUT_ANC_HEADSET) || (devices & AudioSystem::DEVICE_IN_ANC_HEADSET)) { devices = devices | (AudioSystem::DEVICE_OUT_ANC_HEADSET | AudioSystem::DEVICE_IN_ANC_HEADSET); } else if (devices & AudioSystem::DEVICE_OUT_ANC_HEADPHONE) { devices = devices | (AudioSystem::DEVICE_OUT_ANC_HEADPHONE | AudioSystem::DEVICE_IN_BUILTIN_MIC); #endif } else if (devices & AudioSystem::DEVICE_OUT_AUX_DIGITAL) { if (mode == AudioSystem::MODE_IN_CALL) devices = devices | (AudioSystem::DEVICE_IN_BACK_MIC | AudioSystem::DEVICE_OUT_SPEAKER); else devices = devices | (AudioSystem::DEVICE_OUT_AUX_DIGITAL | AudioSystem::DEVICE_IN_BACK_MIC); #ifdef QCOM_PROXY_DEVICE_ENABLED } else if ((devices & AudioSystem::DEVICE_OUT_PROXY) || (devices & AudioSystem::DEVICE_IN_PROXY)) { devices = devices | (AudioSystem::DEVICE_OUT_PROXY | AudioSystem::DEVICE_IN_PROXY); #endif } } #ifdef QCOM_SSR_ENABLED if ((devices & AudioSystem::DEVICE_IN_BUILTIN_MIC) && ( 6 == handle->channels)) { if (!strncmp(handle->useCase, SND_USE_CASE_VERB_HIFI_REC, strlen(SND_USE_CASE_VERB_HIFI_REC)) || !strncmp(handle->useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, strlen(SND_USE_CASE_MOD_CAPTURE_MUSIC))) { ALOGV(" switchDevice , use ssr devices for channels:%d usecase:%s",handle->channels,handle->useCase); s_set_flags(SSRQMIC_FLAG); } } #endif rxDevice = getUCMDevice(devices & AudioSystem::DEVICE_OUT_ALL, 0, NULL); txDevice = getUCMDevice(devices & AudioSystem::DEVICE_IN_ALL, 1, rxDevice); if ((rxDevice != NULL) && (txDevice != NULL)) { if (((strncmp(rxDevice, curRxUCMDevice, MAX_STR_LEN)) || (strncmp(txDevice, curTxUCMDevice, MAX_STR_LEN))) && ((mode == AudioSystem::MODE_IN_CALL) || (mode == AudioSystem::MODE_IN_COMMUNICATION))) inCallDevSwitch = true; } #ifdef QCOM_CSDCLIENT_ENABLED if (platform_is_Fusion3() && (inCallDevSwitch == true)) { if (csd_disable_device == NULL) { ALOGE("dlsym:Error:%s Loading csd_client_disable_device", dlerror()); } else { err = csd_disable_device(); if (err < 0) { ALOGE("csd_client_disable_device, failed, error %d", err); } } } #endif snd_use_case_get(handle->ucMgr, "_verb", (const char **)&use_case); mods_size = snd_use_case_get_list(handle->ucMgr, "_enamods", &mods_list); if (rxDevice != NULL) { if ((strncmp(curRxUCMDevice, "None", 4)) && ((strncmp(rxDevice, curRxUCMDevice, MAX_STR_LEN)) || (inCallDevSwitch == true))) { if ((use_case != NULL) && (strncmp(use_case, SND_USE_CASE_VERB_INACTIVE, strlen(SND_USE_CASE_VERB_INACTIVE)))) { usecase_type = getUseCaseType(use_case); if (usecase_type & USECASE_TYPE_RX) { ALOGD("Deroute use case %s type is %d\n", use_case, usecase_type); strlcpy(useCaseNode.useCase, use_case, MAX_STR_LEN); snd_use_case_set(handle->ucMgr, "_verb", SND_USE_CASE_VERB_INACTIVE); mUseCaseList.push_front(useCaseNode); } } if (mods_size) { for(index = 0; index < mods_size; index++) { usecase_type = getUseCaseType(mods_list[index]); if (usecase_type & USECASE_TYPE_RX) { ALOGD("Deroute use case %s type is %d\n", mods_list[index], usecase_type); strlcpy(useCaseNode.useCase, mods_list[index], MAX_STR_LEN); snd_use_case_set(handle->ucMgr, "_dismod", mods_list[index]); mUseCaseList.push_back(useCaseNode); } } } snd_use_case_set(handle->ucMgr, "_disdev", curRxUCMDevice); } } if (txDevice != NULL) { if ((strncmp(curTxUCMDevice, "None", 4)) && ((strncmp(txDevice, curTxUCMDevice, MAX_STR_LEN)) || (inCallDevSwitch == true))) { if ((use_case != NULL) && (strncmp(use_case, SND_USE_CASE_VERB_INACTIVE, strlen(SND_USE_CASE_VERB_INACTIVE)))) { usecase_type = getUseCaseType(use_case); if ((usecase_type & USECASE_TYPE_TX) && (!(usecase_type & USECASE_TYPE_RX))) { ALOGD("Deroute use case %s type is %d\n", use_case, usecase_type); strlcpy(useCaseNode.useCase, use_case, MAX_STR_LEN); snd_use_case_set(handle->ucMgr, "_verb", SND_USE_CASE_VERB_INACTIVE); mUseCaseList.push_front(useCaseNode); } } if (mods_size) { for(index = 0; index < mods_size; index++) { usecase_type = getUseCaseType(mods_list[index]); if ((usecase_type & USECASE_TYPE_TX) && (!(usecase_type & USECASE_TYPE_RX))) { ALOGD("Deroute use case %s type is %d\n", mods_list[index], usecase_type); strlcpy(useCaseNode.useCase, mods_list[index], MAX_STR_LEN); snd_use_case_set(handle->ucMgr, "_dismod", mods_list[index]); mUseCaseList.push_back(useCaseNode); } } } snd_use_case_set(handle->ucMgr, "_disdev", curTxUCMDevice); } } ALOGD("%s,rxDev:%s, txDev:%s, curRxDev:%s, curTxDev:%s\n", __FUNCTION__, rxDevice, txDevice, curRxUCMDevice, curTxUCMDevice); if (rxDevice != NULL) { snd_use_case_set(handle->ucMgr, "_enadev", rxDevice); strlcpy(curRxUCMDevice, rxDevice, sizeof(curRxUCMDevice)); #ifdef QCOM_FM_ENABLED if (devices & AudioSystem::DEVICE_OUT_FM) s_set_fm_vol(fmVolume); #endif } if (txDevice != NULL) { snd_use_case_set(handle->ucMgr, "_enadev", txDevice); strlcpy(curTxUCMDevice, txDevice, sizeof(curTxUCMDevice)); } for(ALSAUseCaseList::iterator it = mUseCaseList.begin(); it != mUseCaseList.end(); ++it) { ALOGD("Route use case %s\n", it->useCase); if ((use_case != NULL) && (strncmp(use_case, SND_USE_CASE_VERB_INACTIVE, strlen(SND_USE_CASE_VERB_INACTIVE))) && (!strncmp(use_case, it->useCase, MAX_UC_LEN))) { snd_use_case_set(handle->ucMgr, "_verb", it->useCase); } else { snd_use_case_set(handle->ucMgr, "_enamod", it->useCase); } } if (!mUseCaseList.empty()) mUseCaseList.clear(); if (use_case != NULL) { free(use_case); use_case = NULL; } ALOGD("switchDevice: curTxUCMDevivce %s curRxDevDevice %s", curTxUCMDevice, curRxUCMDevice); if (platform_is_Fusion3() && (inCallDevSwitch == true)) { /* get tx acdb id */ memset(&ident,0,sizeof(ident)); strlcpy(ident, "ACDBID/", sizeof(ident)); strlcat(ident, curTxUCMDevice, sizeof(ident)); tx_dev_id = snd_use_case_get(handle->ucMgr, ident, NULL); /* get rx acdb id */ memset(&ident,0,sizeof(ident)); strlcpy(ident, "ACDBID/", sizeof(ident)); strlcat(ident, curRxUCMDevice, sizeof(ident)); rx_dev_id = snd_use_case_get(handle->ucMgr, ident, NULL); if (((rx_dev_id == DEVICE_SPEAKER_MONO_RX_ACDB_ID ) || (rx_dev_id == DEVICE_SPEAKER_STEREO_RX_ACDB_ID )) && tx_dev_id == DEVICE_HANDSET_TX_ACDB_ID) { tx_dev_id = DEVICE_SPEAKER_TX_ACDB_ID; } #ifdef QCOM_CSDCLIENT_ENABLED ALOGV("rx_dev_id=%d, tx_dev_id=%d\n", rx_dev_id, tx_dev_id); if (csd_enable_device == NULL) { ALOGE("dlsym:Error:%s Loading csd_client_enable_device", dlerror()); } else { err = csd_enable_device(rx_dev_id, tx_dev_id, mDevSettingsFlag); if (err < 0) { ALOGE("csd_client_disable_device failed, error %d", err); } } #endif } if (rxDevice != NULL) { free(rxDevice); rxDevice = NULL; } if (txDevice != NULL) { free(txDevice); txDevice = NULL; } } // ---------------------------------------------------------------------------- static status_t s_init(alsa_device_t *module, ALSAHandleList &list) { ALOGV("s_init: Initializing devices for ALSA module"); list.clear(); return NO_ERROR; } static status_t s_open(alsa_handle_t *handle) { char *devName; unsigned flags = 0; int err = NO_ERROR; if(handle->devices & AudioSystem::DEVICE_OUT_AUX_DIGITAL) { err = setHDMIChannelCount(); if(err != OK) { ALOGE("setHDMIChannelCount err = %d", err); return err; } } /* No need to call s_close for LPA as pcm device open and close is handled by LPAPlayer in stagefright */ if((!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_LOW_POWER)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_LPA)) ||(!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_TUNNEL)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_TUNNEL))) { ALOGV("s_open: Opening LPA /Tunnel playback"); return NO_ERROR; } s_close(handle); ALOGV("s_open: handle %p", handle); // ASoC multicomponent requires a valid path (frontend/backend) for // the device to be opened // The PCM stream is opened in blocking mode, per ALSA defaults. The // AudioFlinger seems to assume blocking mode too, so asynchronous mode // should not be used. if ((!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_LOW_POWER)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_LPA)) || (!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_TUNNEL)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_TUNNEL))) { ALOGV("LPA/tunnel use case"); flags |= PCM_MMAP; flags |= DEBUG_ON; } else if ((!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI)) || (!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI2)) || (!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_LOWLATENCY_MUSIC)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_MUSIC2)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_MUSIC))) { ALOGV("Music case"); flags = PCM_OUT; } else { flags = PCM_IN; } if (handle->channels == 1) { flags |= PCM_MONO; } else if (handle->channels == 4 ) { flags |= PCM_QUAD; } else if (handle->channels == 6 ) { #ifdef QCOM_SSR_ENABLED if (!strncmp(handle->useCase, SND_USE_CASE_VERB_HIFI_REC, strlen(SND_USE_CASE_VERB_HIFI_REC)) || !strncmp(handle->useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, strlen(SND_USE_CASE_MOD_CAPTURE_MUSIC))) { flags |= PCM_QUAD; } else { flags |= PCM_5POINT1; } #else flags |= PCM_5POINT1; #endif } else { flags |= PCM_STEREO; } if (deviceName(handle, flags, &devName) < 0) { ALOGE("Failed to get pcm device node: %s", devName); return NO_INIT; } if (devName != NULL) { handle->handle = pcm_open(flags, (char*)devName); } else { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (!handle->handle) { ALOGE("s_open: Failed to initialize ALSA device '%s'", devName); free(devName); return NO_INIT; } handle->handle->flags = flags; err = setHardwareParams(handle); if (err == NO_ERROR) { err = setSoftwareParams(handle); } if(err != NO_ERROR) { ALOGE("Set HW/SW params failed: Closing the pcm stream"); s_standby(handle); } free(devName); return NO_ERROR; } static status_t s_start_voip_call(alsa_handle_t *handle) { char* devName; char* devName1; unsigned flags = 0; int err = NO_ERROR; uint8_t voc_pkt[VOIP_BUFFER_MAX_SIZE]; s_close(handle); flags = PCM_OUT; flags |= PCM_MONO; ALOGV("s_open:s_start_voip_call handle %p", handle); if (deviceName(handle, flags, &devName) < 0) { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (devName != NULL) { handle->handle = pcm_open(flags, (char*)devName); } else { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (!handle->handle) { free(devName); ALOGE("s_open: Failed to initialize ALSA device '%s'", devName); return NO_INIT; } if (!pcm_ready(handle->handle)) { ALOGE(" pcm ready failed"); } handle->handle->flags = flags; err = setHardwareParams(handle); if (err == NO_ERROR) { err = setSoftwareParams(handle); } err = pcm_prepare(handle->handle); if(err != NO_ERROR) { ALOGE("DEVICE_OUT_DIRECTOUTPUT: pcm_prepare failed"); } /* first write required start dsp */ memset(&voc_pkt,0,sizeof(voc_pkt)); pcm_write(handle->handle,&voc_pkt,handle->handle->period_size); handle->rxHandle = handle->handle; free(devName); ALOGV("s_open: DEVICE_IN_COMMUNICATION "); flags = PCM_IN; flags |= PCM_MONO; handle->handle = 0; if (deviceName(handle, flags, &devName1) < 0) { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (devName != NULL) { handle->handle = pcm_open(flags, (char*)devName1); } else { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (!handle->handle) { free(devName); ALOGE("s_open: Failed to initialize ALSA device '%s'", devName); return NO_INIT; } if (!pcm_ready(handle->handle)) { ALOGE(" pcm ready in failed"); } handle->handle->flags = flags; err = setHardwareParams(handle); if (err == NO_ERROR) { err = setSoftwareParams(handle); } err = pcm_prepare(handle->handle); if(err != NO_ERROR) { ALOGE("DEVICE_IN_COMMUNICATION: pcm_prepare failed"); } /* first read required start dsp */ memset(&voc_pkt,0,sizeof(voc_pkt)); pcm_read(handle->handle,&voc_pkt,handle->handle->period_size); return NO_ERROR; } static status_t s_start_voice_call(alsa_handle_t *handle) { char* devName; unsigned flags = 0; int err = NO_ERROR; ALOGV("s_start_voice_call: handle %p", handle); // ASoC multicomponent requires a valid path (frontend/backend) for // the device to be opened flags = PCM_OUT | PCM_MONO; if (deviceName(handle, flags, &devName) < 0) { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (devName != NULL) { handle->handle = pcm_open(flags, (char*)devName); } else { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (!handle->handle) { ALOGE("s_start_voicecall: could not open PCM device"); goto Error; } handle->handle->flags = flags; err = setHardwareParams(handle); if(err != NO_ERROR) { ALOGE("s_start_voice_call: setHardwareParams failed"); goto Error; } err = setSoftwareParams(handle); if(err != NO_ERROR) { ALOGE("s_start_voice_call: setSoftwareParams failed"); goto Error; } err = pcm_prepare(handle->handle); if(err != NO_ERROR) { ALOGE("s_start_voice_call: pcm_prepare failed"); goto Error; } if (ioctl(handle->handle->fd, SNDRV_PCM_IOCTL_START)) { ALOGE("s_start_voice_call:SNDRV_PCM_IOCTL_START failed\n"); goto Error; } // Store the PCM playback device pointer in rxHandle handle->rxHandle = handle->handle; free(devName); // Open PCM capture device flags = PCM_IN | PCM_MONO; if (deviceName(handle, flags, &devName) < 0) { ALOGE("Failed to get pcm device node"); goto Error; } if (devName != NULL) { handle->handle = pcm_open(flags, (char*)devName); } else { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (!handle->handle) { free(devName); goto Error; } handle->handle->flags = flags; err = setHardwareParams(handle); if(err != NO_ERROR) { ALOGE("s_start_voice_call: setHardwareParams failed"); goto Error; } err = setSoftwareParams(handle); if(err != NO_ERROR) { ALOGE("s_start_voice_call: setSoftwareParams failed"); goto Error; } err = pcm_prepare(handle->handle); if(err != NO_ERROR) { ALOGE("s_start_voice_call: pcm_prepare failed"); goto Error; } if (ioctl(handle->handle->fd, SNDRV_PCM_IOCTL_START)) { ALOGE("s_start_voice_call:SNDRV_PCM_IOCTL_START failed\n"); goto Error; } if (platform_is_Fusion3()) { #ifdef QCOM_CSDCLIENT_ENABLED if (csd_start_voice == NULL) { ALOGE("dlsym:Error:%s Loading csd_client_start_voice", dlerror()); } else { err = csd_start_voice(); if (err < 0){ ALOGE("s_start_voice_call: csd_client error %d\n", err); goto Error; } } #endif } free(devName); return NO_ERROR; Error: ALOGE("s_start_voice_call: Failed to initialize ALSA device '%s'", devName); free(devName); s_close(handle); return NO_INIT; } static status_t s_start_fm(alsa_handle_t *handle) { char *devName; unsigned flags = 0; int err = NO_ERROR; ALOGV("s_start_fm: handle %p", handle); // ASoC multicomponent requires a valid path (frontend/backend) for // the device to be opened flags = PCM_OUT | PCM_STEREO; if (deviceName(handle, flags, &devName) < 0) { ALOGE("Failed to get pcm device node"); goto Error; } if (devName != NULL) { handle->handle = pcm_open(flags, (char*)devName); } else { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (!handle->handle) { ALOGE("s_start_fm: could not open PCM device"); goto Error; } handle->handle->flags = flags; err = setHardwareParams(handle); if(err != NO_ERROR) { ALOGE("s_start_fm: setHardwareParams failed"); goto Error; } err = setSoftwareParams(handle); if(err != NO_ERROR) { ALOGE("s_start_fm: setSoftwareParams failed"); goto Error; } err = pcm_prepare(handle->handle); if(err != NO_ERROR) { ALOGE("s_start_fm: setSoftwareParams failed"); goto Error; } if (ioctl(handle->handle->fd, SNDRV_PCM_IOCTL_START)) { ALOGE("s_start_fm: SNDRV_PCM_IOCTL_START failed\n"); goto Error; } // Store the PCM playback device pointer in rxHandle handle->rxHandle = handle->handle; free(devName); // Open PCM capture device flags = PCM_IN | PCM_STEREO; if (deviceName(handle, flags, &devName) < 0) { ALOGE("Failed to get pcm device node"); goto Error; } if (devName != NULL) { handle->handle = pcm_open(flags, (char*)devName); } else { ALOGE("Failed to get pcm device node"); return NO_INIT; } if (!handle->handle) { goto Error; } handle->handle->flags = flags; err = setHardwareParams(handle); if(err != NO_ERROR) { ALOGE("s_start_fm: setHardwareParams failed"); goto Error; } err = setSoftwareParams(handle); if(err != NO_ERROR) { ALOGE("s_start_fm: setSoftwareParams failed"); goto Error; } err = pcm_prepare(handle->handle); if(err != NO_ERROR) { ALOGE("s_start_fm: pcm_prepare failed"); goto Error; } if (ioctl(handle->handle->fd, SNDRV_PCM_IOCTL_START)) { ALOGE("s_start_fm: SNDRV_PCM_IOCTL_START failed\n"); goto Error; } s_set_fm_vol(fmVolume); free(devName); return NO_ERROR; Error: free(devName); s_close(handle); return NO_INIT; } static status_t s_set_fm_vol(int value) { status_t err = NO_ERROR; ALSAControl control("/dev/snd/controlC0"); control.set("Internal FM RX Volume",value,0); fmVolume = value; return err; } static status_t s_set_lpa_vol(int value) { status_t err = NO_ERROR; ALSAControl control("/dev/snd/controlC0"); control.set("LPA RX Volume",value,0); return err; } static status_t s_start(alsa_handle_t *handle) { status_t err = NO_ERROR; if(!handle->handle) { ALOGE("No active PCM driver to start"); return err; } err = pcm_prepare(handle->handle); return err; } static status_t s_close(alsa_handle_t *handle) { int ret; status_t err = NO_ERROR; struct pcm *h = handle->rxHandle; handle->rxHandle = 0; ALOGV("s_close: handle %p h %p", handle, h); if (h) { if ((!strcmp(handle->useCase, SND_USE_CASE_VERB_VOICECALL) || !strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_VOICE)) && platform_is_Fusion3()) { #ifdef QCOM_CSDCLIENT_ENABLED if (csd_stop_voice == NULL) { ALOGE("dlsym:Error:%s Loading csd_client_disable_device", dlerror()); } else { err = csd_stop_voice(); if (err < 0) { ALOGE("s_close: csd_client error %d\n", err); } } #endif } ALOGV("s_close rxHandle\n"); err = pcm_close(h); if(err != NO_ERROR) { ALOGE("s_close: pcm_close failed for rxHandle with err %d", err); } } h = handle->handle; handle->handle = 0; if (h) { ALOGV("s_close handle h %p\n", h); err = pcm_close(h); if(err != NO_ERROR) { ALOGE("s_close: pcm_close failed for handle with err %d", err); } disableDevice(handle); } else if((!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_LOW_POWER)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_LPA)) || (!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_TUNNEL)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_TUNNEL))){ disableDevice(handle); } return err; } /* this is same as s_close, but don't discard the device/mode info. This way we can still close the device, hit idle and power-save, reopen the pcm for the same device/mode after resuming */ static status_t s_standby(alsa_handle_t *handle) { int ret; status_t err = NO_ERROR; struct pcm *h = handle->rxHandle; handle->rxHandle = 0; ALOGV("s_standby: handle %p h %p", handle, h); if (h) { ALOGD("s_standby rxHandle\n"); err = pcm_close(h); if(err != NO_ERROR) { ALOGE("s_standby: pcm_close failed for rxHandle with err %d", err); } } h = handle->handle; handle->handle = 0; if (h) { ALOGV("s_standby handle h %p\n", h); err = pcm_close(h); if(err != NO_ERROR) { ALOGE("s_standby: pcm_close failed for handle with err %d", err); } disableDevice(handle); } else if((!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_LOW_POWER)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_LPA)) || (!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_TUNNEL)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_TUNNEL))) { disableDevice(handle); } return err; } static status_t s_route(alsa_handle_t *handle, uint32_t devices, int mode) { status_t status = NO_ERROR; ALOGD("s_route: devices 0x%x in mode %d", devices, mode); callMode = mode; switchDevice(handle, devices, mode); return status; } int getUseCaseType(const char *useCase) { ALOGD("use case is %s\n", useCase); if (!strncmp(useCase, SND_USE_CASE_VERB_HIFI, MAX_LEN(useCase,SND_USE_CASE_VERB_HIFI)) || !strncmp(useCase, SND_USE_CASE_VERB_HIFI2, MAX_LEN(useCase, SND_USE_CASE_VERB_HIFI2)) || !strncmp(useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC, MAX_LEN(useCase,SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC)) || !strncmp(useCase, SND_USE_CASE_VERB_HIFI_LOW_POWER, MAX_LEN(useCase,SND_USE_CASE_VERB_HIFI_LOW_POWER)) || !strncmp(useCase, SND_USE_CASE_VERB_HIFI_TUNNEL, MAX_LEN(useCase,SND_USE_CASE_VERB_HIFI_TUNNEL)) || !strncmp(useCase, SND_USE_CASE_VERB_HIFI2, MAX_LEN(useCase,SND_USE_CASE_VERB_HIFI2)) || !strncmp(useCase, SND_USE_CASE_VERB_DIGITAL_RADIO, MAX_LEN(useCase,SND_USE_CASE_VERB_DIGITAL_RADIO)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_MUSIC, MAX_LEN(useCase,SND_USE_CASE_MOD_PLAY_MUSIC)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_MUSIC2, MAX_LEN(useCase, SND_USE_CASE_MOD_PLAY_MUSIC2)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_LOWLATENCY_MUSIC, MAX_LEN(useCase,SND_USE_CASE_MOD_PLAY_LOWLATENCY_MUSIC)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_MUSIC2, MAX_LEN(useCase,SND_USE_CASE_MOD_PLAY_MUSIC2)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_LPA, MAX_LEN(useCase,SND_USE_CASE_MOD_PLAY_LPA)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_TUNNEL, MAX_LEN(useCase,SND_USE_CASE_MOD_PLAY_TUNNEL)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_FM, MAX_LEN(useCase,SND_USE_CASE_MOD_PLAY_FM))) { return USECASE_TYPE_RX; } else if (!strncmp(useCase, SND_USE_CASE_VERB_HIFI_REC, MAX_LEN(useCase,SND_USE_CASE_VERB_HIFI_REC)) || !strncmp(useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_REC, MAX_LEN(useCase,SND_USE_CASE_VERB_HIFI_LOWLATENCY_REC)) || !strncmp(useCase, SND_USE_CASE_VERB_FM_REC, MAX_LEN(useCase,SND_USE_CASE_VERB_FM_REC)) || !strncmp(useCase, SND_USE_CASE_VERB_FM_A2DP_REC, MAX_LEN(useCase,SND_USE_CASE_VERB_FM_A2DP_REC)) || !strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, MAX_LEN(useCase,SND_USE_CASE_MOD_CAPTURE_MUSIC)) || !strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_LOWLATENCY_MUSIC, MAX_LEN(useCase,SND_USE_CASE_MOD_CAPTURE_LOWLATENCY_MUSIC)) || !strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_FM, MAX_LEN(useCase,SND_USE_CASE_MOD_CAPTURE_FM)) || !strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_A2DP_FM, MAX_LEN(useCase,SND_USE_CASE_MOD_CAPTURE_A2DP_FM))) { return USECASE_TYPE_TX; } else if (!strncmp(useCase, SND_USE_CASE_VERB_VOICECALL, MAX_LEN(useCase,SND_USE_CASE_VERB_VOICECALL)) || !strncmp(useCase, SND_USE_CASE_VERB_IP_VOICECALL, MAX_LEN(useCase,SND_USE_CASE_VERB_IP_VOICECALL)) || !strncmp(useCase, SND_USE_CASE_VERB_DL_REC, MAX_LEN(useCase,SND_USE_CASE_VERB_DL_REC)) || !strncmp(useCase, SND_USE_CASE_VERB_UL_DL_REC, MAX_LEN(useCase,SND_USE_CASE_VERB_UL_DL_REC)) || !strncmp(useCase, SND_USE_CASE_VERB_INCALL_REC, MAX_LEN(useCase,SND_USE_CASE_VERB_INCALL_REC)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_VOICE, MAX_LEN(useCase,SND_USE_CASE_MOD_PLAY_VOICE)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_VOIP, MAX_LEN(useCase,SND_USE_CASE_MOD_PLAY_VOIP)) || !strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_VOICE_DL, MAX_LEN(useCase,SND_USE_CASE_MOD_CAPTURE_VOICE_DL)) || !strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_VOICE_UL_DL, MAX_LEN(useCase,SND_USE_CASE_MOD_CAPTURE_VOICE_UL_DL)) || !strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_VOICE, MAX_LEN(useCase, SND_USE_CASE_MOD_CAPTURE_VOICE)) || !strncmp(useCase, SND_USE_CASE_VERB_VOLTE, MAX_LEN(useCase,SND_USE_CASE_VERB_VOLTE)) || !strncmp(useCase, SND_USE_CASE_MOD_PLAY_VOLTE, MAX_LEN(useCase, SND_USE_CASE_MOD_PLAY_VOLTE))) { return (USECASE_TYPE_RX | USECASE_TYPE_TX); } else { ALOGE("unknown use case %s\n", useCase); return 0; } } static void disableDevice(alsa_handle_t *handle) { unsigned usecase_type = 0; int i, mods_size; char *useCase; const char **mods_list; snd_use_case_get(handle->ucMgr, "_verb", (const char **)&useCase); if (useCase != NULL) { if (!strncmp(useCase, handle->useCase, MAX_UC_LEN)) { snd_use_case_set(handle->ucMgr, "_verb", SND_USE_CASE_VERB_INACTIVE); } else { snd_use_case_set(handle->ucMgr, "_dismod", handle->useCase); } free(useCase); snd_use_case_get(handle->ucMgr, "_verb", (const char **)&useCase); if (strncmp(useCase, SND_USE_CASE_VERB_INACTIVE, strlen(SND_USE_CASE_VERB_INACTIVE))) usecase_type |= getUseCaseType(useCase); mods_size = snd_use_case_get_list(handle->ucMgr, "_enamods", &mods_list); ALOGV("Number of modifiers %d\n", mods_size); if (mods_size) { for(i = 0; i < mods_size; i++) { ALOGV("index %d modifier %s\n", i, mods_list[i]); usecase_type |= getUseCaseType(mods_list[i]); } } ALOGV("usecase_type is %d\n", usecase_type); if (!(usecase_type & USECASE_TYPE_TX) && (strncmp(curTxUCMDevice, "None", 4))) snd_use_case_set(handle->ucMgr, "_disdev", curTxUCMDevice); if (!(usecase_type & USECASE_TYPE_RX) && (strncmp(curRxUCMDevice, "None", 4))) snd_use_case_set(handle->ucMgr, "_disdev", curRxUCMDevice); } else { ALOGE("Invalid state, no valid use case found to disable"); } free(useCase); } char *getUCMDevice(uint32_t devices, int input, char *rxDevice) { bool is_tmus = s_is_tmus(); if (!input) { if (!(mDevSettingsFlag & TTY_OFF) && (callMode == AudioSystem::MODE_IN_CALL) && ((devices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) || (devices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE))) { #ifdef QCOM_ANC_HEADSET_ENABLED || (devices & AudioSystem::DEVICE_OUT_ANC_HEADSET) || (devices & AudioSystem::DEVICE_OUT_ANC_HEADPHONE))) { #endif if (mDevSettingsFlag & TTY_VCO) { return strdup(SND_USE_CASE_DEV_TTY_HEADSET_RX); } else if (mDevSettingsFlag & TTY_FULL) { return strdup(SND_USE_CASE_DEV_TTY_FULL_RX); } else if (mDevSettingsFlag & TTY_HCO) { return strdup(SND_USE_CASE_DEV_TTY_HANDSET_RX); /* HANDSET RX */ } }else if ((devices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET) || (devices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET)) { return strdup(SND_USE_CASE_DEV_PROXY_RX); /* PROXY RX */ } else if ((devices & AudioSystem::DEVICE_OUT_SPEAKER) && ((devices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) || (devices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE))) { if (mDevSettingsFlag & ANC_FLAG) { return strdup(SND_USE_CASE_DEV_SPEAKER_ANC_HEADSET); /* COMBO SPEAKER+ANC HEADSET RX */ } else { return strdup(SND_USE_CASE_DEV_SPEAKER_HEADSET); /* COMBO SPEAKER+HEADSET RX */ } } else if ((devices & AudioSystem::DEVICE_OUT_SPEAKER) && ((devices & AudioSystem::DEVICE_OUT_AUX_DIGITAL))) { return strdup(SND_USE_CASE_DEV_HDMI_SPEAKER); #ifdef QCOM_ANC_HEADSET_ENABLED } else if ((devices & AudioSystem::DEVICE_OUT_SPEAKER) && ((devices & AudioSystem::DEVICE_OUT_ANC_HEADSET) || (devices & AudioSystem::DEVICE_OUT_ANC_HEADPHONE))) { return strdup(SND_USE_CASE_DEV_SPEAKER_ANC_HEADSET); /* COMBO SPEAKER+ANC HEADSET RX */ } else if ((devices & AudioSystem::DEVICE_OUT_SPEAKER) && (devices & AudioSystem::DEVICE_OUT_FM_TX)) { return strdup(SND_USE_CASE_DEV_SPEAKER_FM_TX); /* COMBO SPEAKER+FM_TX RX */ #endif } else if (devices & AudioSystem::DEVICE_OUT_EARPIECE) { if (callMode == AudioSystem::MODE_IN_CALL) { if(is_tmus) return strdup(SND_USE_CASE_DEV_VOC_EARPIECE_TMUS); /* Voice HANDSET RX for TMUS */ else return strdup(SND_USE_CASE_DEV_VOC_EARPIECE); /* Voice HANDSET RX */ } else return strdup(SND_USE_CASE_DEV_EARPIECE); /* HANDSET RX */ } else if (devices & AudioSystem::DEVICE_OUT_SPEAKER) { if (callMode == AudioSystem::MODE_IN_CALL) { return strdup(SND_USE_CASE_DEV_VOC_SPEAKER); /* Voice SPEAKER RX */ } else return strdup(SND_USE_CASE_DEV_SPEAKER); /* SPEAKER RX */ } else if ((devices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) || (devices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) { if (mDevSettingsFlag & ANC_FLAG) { if (callMode == AudioSystem::MODE_IN_CALL) { return strdup(SND_USE_CASE_DEV_VOC_ANC_HEADSET); /* Voice ANC HEADSET RX */ } else return strdup(SND_USE_CASE_DEV_ANC_HEADSET); /* ANC HEADSET RX */ } else { if (callMode == AudioSystem::MODE_IN_CALL) { return strdup(SND_USE_CASE_DEV_VOC_HEADPHONE); /* Voice HEADSET RX */ } else return strdup(SND_USE_CASE_DEV_HEADPHONES); /* HEADSET RX */ } #ifdef QCOM_ANC_HEADSET_ENABLED } else if ((devices & AudioSystem::DEVICE_OUT_ANC_HEADSET) || (devices & AudioSystem::DEVICE_OUT_ANC_HEADPHONE)) { if (callMode == AudioSystem::MODE_IN_CALL) { return strdup(SND_USE_CASE_DEV_VOC_ANC_HEADSET); /* Voice ANC HEADSET RX */ } else return strdup(SND_USE_CASE_DEV_ANC_HEADSET); /* ANC HEADSET RX */ #endif } else if ((devices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO) || (devices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET) || (devices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT)) { if (btsco_samplerate == BTSCO_RATE_16KHZ) return strdup(SND_USE_CASE_DEV_BTSCO_WB_RX); /* BTSCO RX*/ else return strdup(SND_USE_CASE_DEV_BTSCO_NB_RX); /* BTSCO RX*/ } else if ((devices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP) || (devices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) || #ifdef QCOM_VOIP_ENABLED (devices & AudioSystem::DEVICE_OUT_DIRECTOUTPUT) || #endif (devices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) { /* Nothing to be done, use current active device */ if (strncmp(curRxUCMDevice, "None", 4)) { return strdup(curRxUCMDevice); } } else if (devices & AudioSystem::DEVICE_OUT_AUX_DIGITAL) { return strdup(SND_USE_CASE_DEV_HDMI); /* HDMI RX */ #ifdef QCOM_PROXY_DEVICE_ENABLED } else if (devices & AudioSystem::DEVICE_OUT_PROXY) { return strdup(SND_USE_CASE_DEV_PROXY_RX); /* PROXY RX */ #endif #ifdef QCOM_FM_TX_ENABLED } else if (devices & AudioSystem::DEVICE_OUT_FM_TX) { return strdup(SND_USE_CASE_DEV_FM_TX); /* FM Tx */ #endif } else if (devices & AudioSystem::DEVICE_OUT_DEFAULT) { if (callMode == AudioSystem::MODE_IN_CALL) { return strdup(SND_USE_CASE_DEV_VOC_SPEAKER); /* Voice SPEAKER RX */ } else return strdup(SND_USE_CASE_DEV_SPEAKER); /* SPEAKER RX */ } else { ALOGD("No valid output device: %u", devices); } } else { if (!(mDevSettingsFlag & TTY_OFF) && (callMode == AudioSystem::MODE_IN_CALL) && ((devices & AudioSystem::DEVICE_IN_WIRED_HEADSET) #ifdef QCOM_ANC_HEADSET_ENABLED || (devices & AudioSystem::DEVICE_IN_ANC_HEADSET) #endif )) { if (mDevSettingsFlag & TTY_HCO) { return strdup(SND_USE_CASE_DEV_TTY_HEADSET_TX); } else if (mDevSettingsFlag & TTY_FULL) { return strdup(SND_USE_CASE_DEV_TTY_FULL_TX); } else if (mDevSettingsFlag & TTY_VCO) { if (!strncmp(mic_type, "analog", 6)) { return strdup(SND_USE_CASE_DEV_TTY_HANDSET_ANALOG_TX); } else { return strdup(SND_USE_CASE_DEV_TTY_HANDSET_TX); } } } else if (devices & AudioSystem::DEVICE_IN_BUILTIN_MIC) { if (!strncmp(mic_type, "analog", 6)) { return strdup(SND_USE_CASE_DEV_HANDSET); /* HANDSET TX */ } else { if (mDevSettingsFlag & DMIC_FLAG) { if(callMode == AudioSystem::MODE_IN_CALL) { #ifdef USES_FLUENCE_INCALL if (fluence_mode == FLUENCE_MODE_ENDFIRE) { if(is_tmus) return strdup(SND_USE_CASE_DEV_DUAL_MIC_ENDFIRE_TMUS); /* DUALMIC EF TX */ else return strdup(SND_USE_CASE_DEV_DUAL_MIC_ENDFIRE); /* DUALMIC EF TX */ } else if (fluence_mode == FLUENCE_MODE_BROADSIDE) { return strdup(SND_USE_CASE_DEV_DUAL_MIC_BROADSIDE); /* DUALMIC BS TX */ } else { return strdup(SND_USE_CASE_DEV_HANDSET); /* BUILTIN-MIC TX */ } #endif } if (((rxDevice != NULL) && !strncmp(rxDevice, SND_USE_CASE_DEV_SPEAKER, (strlen(SND_USE_CASE_DEV_SPEAKER)+1))) || !strncmp(curRxUCMDevice, SND_USE_CASE_DEV_SPEAKER, (strlen(SND_USE_CASE_DEV_SPEAKER)+1))) { if (fluence_mode == FLUENCE_MODE_ENDFIRE) { if (input_source == AUDIO_SOURCE_VOICE_RECOGNITION) { // TODO: check if different ACDB settings are needed when speaker is enabled return strdup(SND_USE_CASE_DEV_DUAL_MIC_ENDFIRE_VREC); } else { return strdup(SND_USE_CASE_DEV_SPEAKER_DUAL_MIC_ENDFIRE); } } else if (fluence_mode == FLUENCE_MODE_BROADSIDE) { if (input_source == AUDIO_SOURCE_VOICE_RECOGNITION) { return strdup(SND_USE_CASE_DEV_DUAL_MIC_BROADSIDE_VREC); } else { return strdup(SND_USE_CASE_DEV_SPEAKER_DUAL_MIC_BROADSIDE); } } } else { if (fluence_mode == FLUENCE_MODE_ENDFIRE) { if (input_source == AUDIO_SOURCE_VOICE_RECOGNITION) { return strdup(SND_USE_CASE_DEV_DUAL_MIC_ENDFIRE_VREC); } else { return strdup(SND_USE_CASE_DEV_DUAL_MIC_ENDFIRE); } } else if (fluence_mode == FLUENCE_MODE_BROADSIDE) { if (input_source == AUDIO_SOURCE_VOICE_RECOGNITION) { return strdup(SND_USE_CASE_DEV_DUAL_MIC_BROADSIDE_VREC); } else { return strdup(SND_USE_CASE_DEV_DUAL_MIC_BROADSIDE); } } } } else if (mDevSettingsFlag & QMIC_FLAG){ return strdup(SND_USE_CASE_DEV_QUAD_MIC); } #ifdef QCOM_SSR_ENABLED else if (mDevSettingsFlag & SSRQMIC_FLAG){ ALOGV("return SSRQMIC_FLAG: 0x%x devices:0x%x",mDevSettingsFlag,devices); // Mapping for quad mic input device. return strdup(SND_USE_CASE_DEV_SSR_QUAD_MIC); /* SSR Quad MIC */ } #endif #ifdef SEPERATED_AUDIO_INPUT if(input_source == AUDIO_SOURCE_VOICE_RECOGNITION) { return strdup(SND_USE_CASE_DEV_VOICE_RECOGNITION ); /* VOICE RECOGNITION TX */ } #endif else { return strdup(SND_USE_CASE_DEV_HANDSET); /* BUILTIN-MIC TX */ } } } else if (devices & AudioSystem::DEVICE_IN_AUX_DIGITAL) { return strdup(SND_USE_CASE_DEV_HDMI_TX); /* HDMI TX */ #ifdef QCOM_ANC_HEADSET_ENABLED } else if (devices & AudioSystem::DEVICE_IN_ANC_HEADSET) { return strdup(SND_USE_CASE_DEV_HEADSET); /* HEADSET TX */ #endif } else if (devices & AudioSystem::DEVICE_IN_WIRED_HEADSET) { if (callMode == AudioSystem::MODE_IN_CALL) { return strdup(SND_USE_CASE_DEV_VOC_HEADSET); /* Voice HEADSET TX */ } else return strdup(SND_USE_CASE_DEV_HEADSET); /* HEADSET TX */ } else if (devices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) { if (btsco_samplerate == BTSCO_RATE_16KHZ) return strdup(SND_USE_CASE_DEV_BTSCO_WB_TX); /* BTSCO TX*/ else return strdup(SND_USE_CASE_DEV_BTSCO_NB_TX); /* BTSCO TX*/ #ifdef QCOM_USBAUDIO_ENABLED } else if ((devices & AudioSystem::DEVICE_IN_ANLG_DOCK_HEADSET) || (devices & AudioSystem::DEVICE_IN_PROXY)) { return strdup(SND_USE_CASE_DEV_PROXY_TX); /* PROXY TX */ #endif } else if ((devices & AudioSystem::DEVICE_IN_COMMUNICATION) || (devices & AudioSystem::DEVICE_IN_VOICE_CALL)) { /* Nothing to be done, use current active device */ if (strncmp(curTxUCMDevice, "None", 4)) { return strdup(curTxUCMDevice); } #ifdef QCOM_FM_ENABLED } else if ((devices & AudioSystem::DEVICE_IN_FM_RX) || (devices & AudioSystem::DEVICE_IN_FM_RX_A2DP)) { /* Nothing to be done, use current tx device or set dummy device */ if (strncmp(curTxUCMDevice, "None", 4)) { return strdup(curTxUCMDevice); } else { return strdup(SND_USE_CASE_DEV_DUMMY_TX); } #endif } else if ((devices & AudioSystem::DEVICE_IN_AMBIENT) || (devices & AudioSystem::DEVICE_IN_BACK_MIC)) { ALOGI("No proper mapping found with UCM device list, setting default"); if (!strncmp(mic_type, "analog", 6)) { return strdup(SND_USE_CASE_DEV_HANDSET); /* HANDSET TX */ } else { if (callMode == AudioSystem::MODE_IN_CALL) { return strdup(SND_USE_CASE_DEV_VOC_LINE); /* Voice BUILTIN-MIC TX */ #ifdef SEPERATED_AUDIO_INPUT } else if(input_source == AUDIO_SOURCE_CAMCORDER) { return strdup(SND_USE_CASE_DEV_CAMCORDER_TX ); /* CAMCORDER TX */ #endif } else return strdup(SND_USE_CASE_DEV_LINE); /* BUILTIN-MIC TX */ } } else { ALOGD("No valid input device: %u", devices); } } return NULL; } void s_set_voice_volume(int vol) { int err = 0; ALOGV("s_set_voice_volume: volume %d", vol); ALSAControl control("/dev/snd/controlC0"); control.set("Voice Rx Volume", vol, 0); if (platform_is_Fusion3()) { #ifdef QCOM_CSDCLIENT_ENABLED if (csd_volume == NULL) { ALOGE("dlsym:Error:%s Loading csd_client_volume", dlerror()); } else { err = csd_volume(vol); if (err < 0) { ALOGE("s_set_voice_volume: csd_client error %d", err); } } #endif } } void s_set_volte_volume(int vol) { ALOGV("s_set_volte_volume: volume %d", vol); ALSAControl control("/dev/snd/controlC0"); control.set("VoLTE Rx Volume", vol, 0); } void s_set_voip_volume(int vol) { ALOGV("s_set_voip_volume: volume %d", vol); ALSAControl control("/dev/snd/controlC0"); control.set("Voip Rx Volume", vol, 0); } void s_set_mic_mute(int state) { int err = 0; ALOGV("s_set_mic_mute: state %d", state); ALSAControl control("/dev/snd/controlC0"); control.set("Voice Tx Mute", state, 0); if (platform_is_Fusion3()) { #ifdef QCOM_CSDCLIENT_ENABLED if (csd_mic_mute == NULL) { ALOGE("dlsym:Error:%s Loading csd_mic_mute", dlerror()); } else { err=csd_mic_mute(state); if (err < 0) { ALOGE("s_set_mic_mute: csd_client error %d", err); } } #endif } } void s_set_volte_mic_mute(int state) { ALOGV("s_set_volte_mic_mute: state %d", state); ALSAControl control("/dev/snd/controlC0"); control.set("VoLTE Tx Mute", state, 0); } void s_set_voip_mic_mute(int state) { ALOGV("s_set_voip_mic_mute: state %d", state); ALSAControl control("/dev/snd/controlC0"); control.set("Voip Tx Mute", state, 0); } void s_set_voip_config(int mode, int rate) { ALOGV("s_set_voip_config: mode %d,rate %d", mode, rate); ALSAControl control("/dev/snd/controlC0"); char** setValues; setValues = (char**)malloc(2*sizeof(char*)); if (setValues == NULL) { return; } setValues[0] = (char*)malloc(4*sizeof(char)); if (setValues[0] == NULL) { free(setValues); return; } setValues[1] = (char*)malloc(8*sizeof(char)); if (setValues[1] == NULL) { free(setValues); free(setValues[0]); return; } sprintf(setValues[0], "%d",mode); sprintf(setValues[1], "%d",rate); control.setext("Voip Mode Rate Config", 2, setValues); free(setValues[1]); free(setValues[0]); free(setValues); return; } void s_set_btsco_rate(int rate) { btsco_samplerate = rate; } void s_enable_wide_voice(bool flag) { int err = 0; ALOGV("s_enable_wide_voice: flag %d", flag); ALSAControl control("/dev/snd/controlC0"); if(flag == true) { control.set("Widevoice Enable", 1, 0); } else { control.set("Widevoice Enable", 0, 0); } if (platform_is_Fusion3()) { #ifdef QCOM_CSDCLIENT_ENABLED if (csd_wide_voice == NULL) { ALOGE("dlsym:Error:%s Loading csd_wide_voice", dlerror()); } else { err = csd_wide_voice(flag); if (err < 0) { ALOGE("enableWideVoice: csd_client_wide_voice error %d", err); } } #endif } } void s_set_voc_rec_mode(uint8_t mode) { ALOGV("s_set_voc_rec_mode: mode %d", mode); ALSAControl control("/dev/snd/controlC0"); control.set("Incall Rec Mode", mode, 0); } void s_enable_fens(bool flag) { int err = 0; ALOGV("s_enable_fens: flag %d", flag); ALSAControl control("/dev/snd/controlC0"); if(flag == true) { control.set("FENS Enable", 1, 0); } else { control.set("FENS Enable", 0, 0); } if (platform_is_Fusion3()) { #ifdef QCOM_CSDCLIENT_ENABLED if (csd_fens == NULL) { ALOGE("dlsym:Error:%s Loading csd_fens", dlerror()); } else { err = csd_fens(flag); if (err < 0) { ALOGE("s_enable_fens: csd_client error %d", err); } } #endif } } void s_enable_slow_talk(bool flag) { int err = 0; ALOGV("s_enable_slow_talk: flag %d", flag); ALSAControl control("/dev/snd/controlC0"); if(flag == true) { control.set("Slowtalk Enable", 1, 0); } else { control.set("Slowtalk Enable", 0, 0); } if (platform_is_Fusion3()) { #ifdef QCOM_CSDCLIENT_ENABLED if (csd_slow_talk == NULL) { ALOGE("dlsym:Error:%s Loading csd_slow_talk", dlerror()); } else { err = csd_slow_talk(flag); if (err < 0) { ALOGE("s_enable_slow_talk: csd_client error %d", err); } } #endif } } void s_set_flags(uint32_t flags) { ALOGV("s_set_flags: flags %d", flags); mDevSettingsFlag = flags; } static status_t s_set_compressed_vol(int value) { status_t err = NO_ERROR; ALSAControl control("/dev/snd/controlC0"); control.set("COMPRESSED RX Volume",value,0); return err; } #ifdef SEPERATED_AUDIO_INPUT void s_setInput(int input) { input_source = input; ALOGD("s_setInput() : input_source = %d",input_source); } #endif #ifdef QCOM_CSDCLIENT_ENABLED static void s_set_csd_handle(void* handle) { csd_handle = static_cast<void*>(handle); ALOGI("%s csd_handle: %p", __func__, csd_handle); csd_disable_device = (int (*)())::dlsym(csd_handle,"csd_client_disable_device"); csd_enable_device = (int (*)(int,int,uint32_t))::dlsym(csd_handle,"csd_client_enable_device"); csd_start_voice = (int (*)())::dlsym(csd_handle,"csd_client_start_voice"); csd_stop_voice = (int (*)())::dlsym(csd_handle,"csd_client_stop_voice"); csd_volume = (int (*)(int))::dlsym(csd_handle,"csd_client_volume"); csd_mic_mute = (int (*)(int))::dlsym(csd_handle,"csd_client_mic_mute"); csd_wide_voice = (int (*)(uint8_t))::dlsym(csd_handle,"csd_client_wide_voice"); csd_fens = (int (*)(uint8_t))::dlsym(csd_handle,"csd_client_fens"); csd_slow_talk = (int (*)(uint8_t))::dlsym(csd_handle,"csd_client_slow_talk"); } #endif static bool s_is_tmus() { char value[128]; bool ret = false; if (mccmnc == 0) { property_get("gsm.sim.operator.numeric",value,"0"); mccmnc = atoi(value); } ALOGD("%s: mnc_mcc : %d", __FUNCTION__, mccmnc); switch(mccmnc) { //TMUS MCC(310), MNC(490, 260, 026) case 310490: case 310260: case 310026: ret = true; break; default: ret = false; break; } return ret; } }