diff options
| author | Vineeta Srivastava <vsrivastava@google.com> | 2014-06-19 14:21:42 -0700 |
|---|---|---|
| committer | Vineeta Srivastava <vsrivastava@google.com> | 2014-06-19 14:21:42 -0700 |
| commit | 4b89e37ad290ef955abf8ac1d151728303311345 (patch) | |
| tree | e239ce78f88eecd7f69189f01c30e6ff7b106404 /hal | |
| parent | 299760a41231bd0f6d9991fb189977347365c72b (diff) | |
| download | android_hardware_qcom_audio-4b89e37ad290ef955abf8ac1d151728303311345.tar.gz android_hardware_qcom_audio-4b89e37ad290ef955abf8ac1d151728303311345.tar.bz2 android_hardware_qcom_audio-4b89e37ad290ef955abf8ac1d151728303311345.zip | |
Revert "Revert "hal: Add support for IMS calls""
This reverts commit a609e8ebdfeca875b6d35ccfb3fb8b87710f3499.
Diffstat (limited to 'hal')
| -rw-r--r-- | hal/Android.mk | 9 | ||||
| -rw-r--r-- | hal/audio_extn/hfp.c | 14 | ||||
| -rw-r--r-- | hal/audio_hw.c | 306 | ||||
| -rw-r--r-- | hal/audio_hw.h | 32 | ||||
| -rw-r--r-- | hal/msm8960/platform.c | 43 | ||||
| -rw-r--r-- | hal/msm8960/platform.h | 2 | ||||
| -rw-r--r-- | hal/msm8974/platform.c | 301 | ||||
| -rw-r--r-- | hal/msm8974/platform.h | 26 | ||||
| -rw-r--r-- | hal/platform_api.h | 14 | ||||
| -rw-r--r-- | hal/voice.c | 451 | ||||
| -rw-r--r-- | hal/voice.h | 88 | ||||
| -rw-r--r-- | hal/voice_extn/voice_extn.c | 585 | ||||
| -rw-r--r-- | hal/voice_extn/voice_extn.h | 99 |
13 files changed, 1640 insertions, 330 deletions
diff --git a/hal/Android.mk b/hal/Android.mk index 910edbe6..30abdf31 100644 --- a/hal/Android.mk +++ b/hal/Android.mk @@ -20,6 +20,7 @@ endif LOCAL_SRC_FILES := \ audio_hw.c \ + voice.c \ $(AUDIO_PLATFORM)/platform.c LOCAL_SHARED_LIBRARIES := \ @@ -36,13 +37,19 @@ LOCAL_C_INCLUDES += \ $(call include-path-for, audio-route) \ $(call include-path-for, audio-effects) \ $(LOCAL_PATH)/$(AUDIO_PLATFORM) \ - $(LOCAL_PATH)/audio_extn + $(LOCAL_PATH)/audio_extn \ + $(LOCAL_PATH)/voice_extn ifneq ($(filter msm8084,$(TARGET_BOARD_PLATFORM)),) LOCAL_SHARED_LIBRARIES += libmdmdetect LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/libmdmdetect/inc endif +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_MULTI_VOICE_SESSIONS)),true) + LOCAL_CFLAGS += -DMULTI_VOICE_SESSION_ENABLED + LOCAL_SRC_FILES += voice_extn/voice_extn.c +endif + ifeq ($(strip $(AUDIO_FEATURE_ENABLED_HFP)),true) LOCAL_CFLAGS += -DHFP_ENABLED LOCAL_SRC_FILES += audio_extn/hfp.c diff --git a/hal/audio_extn/hfp.c b/hal/audio_extn/hfp.c index aa5d2099..a1087309 100644 --- a/hal/audio_extn/hfp.c +++ b/hal/audio_extn/hfp.c @@ -144,8 +144,8 @@ static int32_t start_hfp(struct audio_device *adev, __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__, SOUND_CARD, pcm_dev_rx_id); - hfpmod.hfp_sco_rx = pcm_open(SOUND_CARD, + __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)) { @@ -154,8 +154,8 @@ static int32_t start_hfp(struct audio_device *adev, goto exit; } ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)", - __func__, SOUND_CARD, pcm_dev_tx_id); - hfpmod.hfp_pcm_rx = pcm_open(SOUND_CARD, + __func__, adev->snd_card, pcm_dev_tx_id); + 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)) { @@ -163,7 +163,7 @@ static int32_t start_hfp(struct audio_device *adev, ret = -EIO; goto exit; } - hfpmod.hfp_sco_tx = pcm_open(SOUND_CARD, + 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)) { @@ -172,8 +172,8 @@ static int32_t start_hfp(struct audio_device *adev, goto exit; } ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)", - __func__, SOUND_CARD, pcm_dev_tx_id); - hfpmod.hfp_pcm_tx = pcm_open(SOUND_CARD, + __func__, adev->snd_card, pcm_dev_tx_id); + 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)) { diff --git a/hal/audio_hw.c b/hal/audio_hw.c index eb092b04..2179b865 100644 --- a/hal/audio_hw.c +++ b/hal/audio_hw.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013-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. @@ -48,6 +48,7 @@ #include "audio_extn.h" #include "platform_api.h" #include <platform.h> +#include "voice_extn.h" #include "sound/compress_params.h" @@ -104,24 +105,23 @@ struct pcm_config pcm_config_audio_capture = { .format = PCM_FORMAT_S16_LE, }; -struct pcm_config pcm_config_voice_call = { - .channels = 1, - .rate = 8000, - .period_size = 160, - .period_count = 2, - .format = PCM_FORMAT_S16_LE, -}; - -static const char * const use_case_table[AUDIO_USECASE_MAX] = { +const char * const use_case_table[AUDIO_USECASE_MAX] = { [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = "deep-buffer-playback", [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = "low-latency-playback", [USECASE_AUDIO_PLAYBACK_MULTI_CH] = "multi-channel-playback", + [USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback", + [USECASE_AUDIO_RECORD] = "audio-record", [USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record", + [USECASE_AUDIO_HFP_SCO] = "hfp-sco", [USECASE_AUDIO_HFP_SCO_WB] = "hfp-sco-wb", + [USECASE_VOICE_CALL] = "voice-call", - [USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback", + [USECASE_VOICE2_CALL] = "voice2-call", + [USECASE_VOLTE_CALL] = "volte-call", + [USECASE_QCHAT_CALL] = "qchat-call", + [USECASE_VOWLAN_CALL] = "vowlan-call", }; @@ -167,6 +167,19 @@ static int get_snd_codec_id(audio_format_t format) return id; } +int pcm_ioctl(void *pcm, int request, ...) +{ + va_list ap; + void * arg; + int pcm_fd = *(int*)pcm; + + va_start(ap, request); + arg = va_arg(ap, void *); + va_end(ap); + + return ioctl(pcm_fd, request, arg); +} + int enable_audio_route(struct audio_device *adev, struct audio_usecase *usecase) { @@ -216,7 +229,7 @@ int disable_audio_route(struct audio_device *adev, } int enable_snd_device(struct audio_device *adev, - snd_device_t snd_device) + snd_device_t snd_device) { if (snd_device < SND_DEVICE_MIN || snd_device >= SND_DEVICE_MAX) { @@ -244,7 +257,7 @@ int enable_snd_device(struct audio_device *adev, } int disable_snd_device(struct audio_device *adev, - snd_device_t snd_device) + snd_device_t snd_device) { if (snd_device < SND_DEVICE_MIN || snd_device >= SND_DEVICE_MAX) { @@ -373,6 +386,12 @@ static void check_and_route_capture_usecases(struct audio_device *adev, usecase = node_to_item(node, struct audio_usecase, list); if (switch_device[usecase->id]) { disable_snd_device(adev, usecase->in_snd_device); + } + } + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (switch_device[usecase->id]) { enable_snd_device(adev, snd_device); } } @@ -390,7 +409,6 @@ static void check_and_route_capture_usecases(struct audio_device *adev, } } - /* must be called with hw device mutex locked */ static int read_hdmi_channel_masks(struct stream_out *out) { @@ -465,9 +483,10 @@ int select_devices(struct audio_device *adev, * usecase. This is to avoid switching devices for voice call when * check_usecases_codec_backend() is called below. */ - if (adev->in_call) { + if (voice_is_in_call(adev)) { vc_usecase = get_usecase_from_list(adev, USECASE_VOICE_CALL); - if (vc_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) { + if ((vc_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) || + (usecase->devices == AUDIO_DEVICE_IN_VOICE_CALL)) { in_snd_device = vc_usecase->in_snd_device; out_snd_device = vc_usecase->out_snd_device; } @@ -638,8 +657,8 @@ int start_input_stream(struct stream_in *in) select_devices(adev, in->usecase); ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d", - __func__, SOUND_CARD, in->pcm_device_id, in->config.channels); - in->pcm = pcm_open(SOUND_CARD, in->pcm_device_id, + __func__, adev->snd_card, in->pcm_device_id, in->config.channels); + in->pcm = pcm_open(adev->snd_card, in->pcm_device_id, PCM_IN, &in->config); if (in->pcm && !pcm_is_ready(in->pcm)) { ALOGE("%s: %s", __func__, pcm_get_error(in->pcm)); @@ -946,9 +965,9 @@ int start_output_stream(struct stream_out *out) select_devices(adev, out->usecase); ALOGV("%s: Opening PCM device card_id(%d) device_id(%d) format(%#x)", - __func__, SOUND_CARD, out->pcm_device_id, out->config.format); + __func__, adev->snd_card, out->pcm_device_id, out->config.format); if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) { - out->pcm = pcm_open(SOUND_CARD, out->pcm_device_id, + out->pcm = pcm_open(adev->snd_card, out->pcm_device_id, PCM_OUT | PCM_MONOTONIC, &out->config); if (out->pcm && !pcm_is_ready(out->pcm)) { ALOGE("%s: %s", __func__, pcm_get_error(out->pcm)); @@ -959,7 +978,7 @@ int start_output_stream(struct stream_out *out) } } else { out->pcm = NULL; - out->compr = compress_open(SOUND_CARD, out->pcm_device_id, + out->compr = compress_open(adev->snd_card, out->pcm_device_id, COMPRESS_IN, &out->compr_config); if (out->compr && !is_compress_ready(out->compr)) { ALOGE("%s: %s", __func__, compress_get_error(out->compr)); @@ -982,120 +1001,6 @@ error_config: return ret; } -static int stop_voice_call(struct audio_device *adev) -{ - int i, ret = 0; - struct audio_usecase *uc_info; - - ALOGV("%s: enter", __func__); - adev->in_call = false; - - ret = platform_stop_voice_call(adev->platform); - - /* 1. Close the PCM devices */ - if (adev->voice_call_rx) { - pcm_close(adev->voice_call_rx); - adev->voice_call_rx = NULL; - } - if (adev->voice_call_tx) { - pcm_close(adev->voice_call_tx); - adev->voice_call_tx = NULL; - } - - uc_info = get_usecase_from_list(adev, USECASE_VOICE_CALL); - if (uc_info == NULL) { - ALOGE("%s: Could not find the usecase (%d) in the list", - __func__, USECASE_VOICE_CALL); - 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); - - list_remove(&uc_info->list); - free(uc_info); - - ALOGV("%s: exit: status(%d)", __func__, ret); - return ret; -} - -static int start_voice_call(struct audio_device *adev) -{ - int i, ret = 0; - struct audio_usecase *uc_info; - int pcm_dev_rx_id, pcm_dev_tx_id; - - ALOGV("%s: enter", __func__); - - uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); - uc_info->id = USECASE_VOICE_CALL; - uc_info->type = VOICE_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); - - select_devices(adev, USECASE_VOICE_CALL); - - 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); - - if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) { - ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)", - __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id); - ret = -EIO; - goto error_start_voice; - } - - ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)", - __func__, SOUND_CARD, pcm_dev_rx_id); - adev->voice_call_rx = pcm_open(SOUND_CARD, - pcm_dev_rx_id, - PCM_OUT | PCM_MONOTONIC, &pcm_config_voice_call); - if (adev->voice_call_rx && !pcm_is_ready(adev->voice_call_rx)) { - ALOGE("%s: %s", __func__, pcm_get_error(adev->voice_call_rx)); - ret = -EIO; - goto error_start_voice; - } - - ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)", - __func__, SOUND_CARD, pcm_dev_tx_id); - adev->voice_call_tx = pcm_open(SOUND_CARD, - pcm_dev_tx_id, - PCM_IN, &pcm_config_voice_call); - if (adev->voice_call_tx && !pcm_is_ready(adev->voice_call_tx)) { - ALOGE("%s: %s", __func__, pcm_get_error(adev->voice_call_tx)); - ret = -EIO; - goto error_start_voice; - } - - /* set cached volume */ - set_voice_volume_l(adev, adev->voice_volume); - - pcm_start(adev->voice_call_rx); - pcm_start(adev->voice_call_tx); - - ret = platform_start_voice_call(adev->platform); - if (ret < 0) { - ALOGE("%s: platform_start_voice_call error %d\n", __func__, ret); - goto error_start_voice; - } - adev->in_call = true; - return 0; - -error_start_voice: - stop_voice_call(adev); - - ALOGD("%s: exit: status(%d)", __func__, ret); - return ret; -} - static int check_input_parameters(uint32_t sample_rate, audio_format_t format, int channel_count) @@ -1320,18 +1225,21 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) if (!out->standby) select_devices(adev, out->usecase); - if ((adev->mode == AUDIO_MODE_IN_CALL) && !adev->in_call && + if ((adev->mode == AUDIO_MODE_IN_CALL) && + !voice_is_in_call(adev) && (out == adev->primary_output)) { - start_voice_call(adev); - } else if ((adev->mode == AUDIO_MODE_IN_CALL) && adev->in_call && - (out == adev->primary_output)) { - select_devices(adev, USECASE_VOICE_CALL); + ret = voice_start_call(adev); + } else if ((adev->mode == AUDIO_MODE_IN_CALL) && + voice_is_in_call(adev) && + (out == adev->primary_output)) { + voice_update_devices_for_all_voice_usecases(adev); } } - if ((adev->mode == AUDIO_MODE_NORMAL) && adev->in_call && + if ((adev->mode == AUDIO_MODE_NORMAL) && + voice_is_in_call(adev) && (out == adev->primary_output)) { - stop_voice_call(adev); + ret = voice_stop_call(adev); } pthread_mutex_unlock(&adev->lock); @@ -1794,7 +1702,7 @@ static ssize_t in_read(struct audio_stream_in *stream, void *buffer, * Instead of writing zeroes here, we could trust the hardware * to always provide zeroes when muted. */ - if (ret == 0 && adev->mic_mute) + if (ret == 0 && voice_get_mic_mute(adev) && !voice_is_in_call(adev)) memset(buffer, 0, bytes); exit: @@ -2074,32 +1982,14 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) int ret; int status = 0; - ALOGV("%s: enter: %s", __func__, kvpairs); + ALOGD("%s: enter: %s", __func__, kvpairs); - parms = str_parms_create_str(kvpairs); - ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value)); - if (ret >= 0) { - int tty_mode; - - if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0) - tty_mode = TTY_MODE_OFF; - else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0) - tty_mode = TTY_MODE_VCO; - else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0) - tty_mode = TTY_MODE_HCO; - else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0) - tty_mode = TTY_MODE_FULL; - else - return -EINVAL; + pthread_mutex_lock(&adev->lock); - pthread_mutex_lock(&adev->lock); - if (tty_mode != adev->tty_mode) { - adev->tty_mode = tty_mode; - adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode; - if (adev->in_call) - select_devices(adev, USECASE_VOICE_CALL); - } - pthread_mutex_unlock(&adev->lock); + parms = str_parms_create_str(kvpairs); + status = voice_set_parameters(adev, parms); + if (status != 0) { + goto done; } ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_NREC, value, sizeof(value)); @@ -2140,7 +2030,6 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) status = -EINVAL; } if (status == 0) { - pthread_mutex_lock(&adev->lock); if (adev->speaker_lr_swap != reverse_speakers) { adev->speaker_lr_swap = reverse_speakers; // only update the selected device if there is active pcm playback @@ -2154,19 +2043,18 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) } } } - pthread_mutex_unlock(&adev->lock); } } ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB, value, sizeof(value)); if (ret >= 0) { - pthread_mutex_lock(&adev->lock); adev->bt_wb_speech_enabled = !strcmp(value, AUDIO_PARAMETER_VALUE_ON); - pthread_mutex_unlock(&adev->lock); } audio_extn_hfp_set_parameters(adev, parms); +done: str_parms_destroy(parms); + pthread_mutex_unlock(&adev->lock); ALOGV("%s: exit with code(%d)", __func__, status); return status; } @@ -2174,7 +2062,21 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) static char* adev_get_parameters(const struct audio_hw_device *dev, const char *keys) { - return strdup(""); + struct audio_device *adev = (struct audio_device *)dev; + struct str_parms *reply = str_parms_create(); + struct str_parms *query = str_parms_create_str(keys); + char *str; + + pthread_mutex_lock(&adev->lock); + + voice_get_parameters(adev, query, reply); + str = str_parms_to_str(reply); + str_parms_destroy(query); + str_parms_destroy(reply); + + pthread_mutex_unlock(&adev->lock); + ALOGV("%s: exit: returns - %s", __func__, str); + return str; } static int adev_init_check(const struct audio_hw_device *dev __unused) @@ -2182,39 +2084,15 @@ static int adev_init_check(const struct audio_hw_device *dev __unused) return 0; } -/* always called with adev lock held */ -static int set_voice_volume_l(struct audio_device *adev, float volume) -{ - int vol, err = 0; - - if (adev->mode == AUDIO_MODE_IN_CALL) { - if (volume < 0.0) { - volume = 0.0; - } else if (volume > 1.0) { - volume = 1.0; - } - - vol = lrint(volume * 100.0); - - // Voice volume levels from android are mapped to driver volume levels as follows. - // 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0 - // So adjust the volume to get the correct volume index in driver - vol = 100 - vol; - - err = platform_set_voice_volume(adev->platform, vol); - } - return err; -} - static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) { int ret; struct audio_device *adev = (struct audio_device *)dev; + pthread_mutex_lock(&adev->lock); - /* cache volume */ - adev->voice_volume = volume; - ret = set_voice_volume_l(adev, adev->voice_volume); + ret = voice_set_volume(adev, volume); pthread_mutex_unlock(&adev->lock); + return ret; } @@ -2245,6 +2123,7 @@ static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) pthread_mutex_lock(&adev->lock); if (adev->mode != mode) { + ALOGD("%s: mode %d\n", __func__, mode); adev->mode = mode; } pthread_mutex_unlock(&adev->lock); @@ -2253,23 +2132,20 @@ static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) { + int ret; struct audio_device *adev = (struct audio_device *)dev; - int err = 0; + ALOGD("%s: state %d\n", __func__, state); pthread_mutex_lock(&adev->lock); - adev->mic_mute = state; - - err = platform_set_mic_mute(adev->platform, state); + ret = voice_set_mic_mute(adev, state); pthread_mutex_unlock(&adev->lock); - return err; + + return ret; } static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) { - struct audio_device *adev = (struct audio_device *)dev; - - *state = adev->mic_mute; - + *state = voice_get_mic_mute((struct audio_device *)dev); return 0; } @@ -2289,7 +2165,7 @@ static int adev_open_input_stream(struct audio_hw_device *dev, { struct audio_device *adev = (struct audio_device *)dev; struct stream_in *in; - int ret, buffer_size, frame_size; + int ret = 0, buffer_size, frame_size; int channel_count = popcount(config->channel_mask); ALOGV("%s: enter", __func__); @@ -2299,6 +2175,8 @@ static int adev_open_input_stream(struct audio_hw_device *dev, in = (struct stream_in *)calloc(1, sizeof(struct stream_in)); + pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL); + in->stream.common.get_sample_rate = in_get_sample_rate; in->stream.common.set_sample_rate = in_set_sample_rate; in->stream.common.get_buffer_size = in_get_buffer_size; @@ -2415,7 +2293,7 @@ static int adev_verify_devices(struct audio_device *adev) size_t i; unsigned dir; - const unsigned card_id = SOUND_CARD; + const unsigned card_id = adev->snd_card; char info[512]; /* for possible debug info */ for (dir = 0; dir < 2; ++dir) { @@ -2539,6 +2417,8 @@ static int adev_open(const hw_module_t *module, const char *name, adev = calloc(1, sizeof(struct audio_device)); + pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL); + adev->device.common.tag = HARDWARE_DEVICE_TAG; adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0; adev->device.common.module = (struct hw_module_t *)module; @@ -2567,15 +2447,11 @@ static int adev_open(const hw_module_t *module, const char *name, adev->mode = AUDIO_MODE_NORMAL; adev->active_input = NULL; adev->primary_output = NULL; - adev->voice_call_rx = NULL; - adev->voice_call_tx = NULL; - adev->voice_volume = 1.0f; - adev->tty_mode = TTY_MODE_OFF; adev->bluetooth_nrec = true; - adev->in_call = false; adev->acdb_settings = TTY_MODE_OFF; /* adev->cur_hdmi_channels = 0; by calloc() */ adev->snd_dev_ref_cnt = calloc(SND_DEVICE_MAX, sizeof(int)); + voice_init(adev); list_init(&adev->usecase_list); pthread_mutex_unlock(&adev->lock); diff --git a/hal/audio_hw.h b/hal/audio_hw.h index 37805ab8..62bc100a 100644 --- a/hal/audio_hw.h +++ b/hal/audio_hw.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013-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. @@ -25,6 +25,7 @@ #include <tinycompress/tinycompress.h> #include <audio_route/audio_route.h> +#include "voice.h" #define VISUALIZER_LIBRARY_PATH "/system/lib/soundfx/libqcomvisualizer.so" @@ -65,6 +66,15 @@ typedef enum { USECASE_AUDIO_RECORD_LOW_LATENCY, USECASE_VOICE_CALL, + + /* Voice extension usecases */ + USECASE_VOICE2_CALL, + USECASE_VOLTE_CALL, + USECASE_QCHAT_CALL, + USECASE_VOWLAN_CALL, + USECASE_INCALL_REC_UPLINK, + USECASE_INCALL_REC_DOWNLINK, + USECASE_INCALL_REC_UPLINK_AND_DOWNLINK, AUDIO_USECASE_MAX } audio_usecase_t; @@ -181,22 +191,18 @@ struct audio_device { audio_mode_t mode; struct stream_in *active_input; struct stream_out *primary_output; - int in_call; - float voice_volume; - bool mic_mute; - int tty_mode; bool bluetooth_nrec; bool screen_off; - struct pcm *voice_call_rx; - struct pcm *voice_call_tx; int *snd_dev_ref_cnt; struct listnode usecase_list; struct audio_route *audio_route; int acdb_settings; bool speaker_lr_swap; + struct voice voice; unsigned int cur_hdmi_channels; bool bt_wb_speech_enabled; + int snd_card; void *platform; void *visualizer_lib; @@ -212,19 +218,31 @@ struct audio_device { struct pcm_params *use_case_table[AUDIO_USECASE_MAX]; }; +int pcm_ioctl(void *pcm, int request, ...); + int select_devices(struct audio_device *adev, audio_usecase_t uc_id); + int disable_audio_route(struct audio_device *adev, struct audio_usecase *usecase); + int disable_snd_device(struct audio_device *adev, snd_device_t snd_device); + int enable_snd_device(struct audio_device *adev, snd_device_t snd_device); + int enable_audio_route(struct audio_device *adev, struct audio_usecase *usecase); + struct audio_usecase *get_usecase_from_list(struct audio_device *adev, audio_usecase_t uc_id); +#define LITERAL_TO_STRING(x) #x +#define CHECK(condition) LOG_ALWAYS_FATAL_IF(!(condition), "%s",\ + __FILE__ ":" LITERAL_TO_STRING(__LINE__)\ + " ASSERT_FATAL(" #condition ") failed.") + /* * NOTE: when multiple mutexes have to be acquired, always take the * stream_in or stream_out mutex first, followed by the audio_device mutex. diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c index ef804d2d..a0821b63 100644 --- a/hal/msm8960/platform.c +++ b/hal/msm8960/platform.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013-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. @@ -482,7 +482,7 @@ int platform_switch_voice_call_device_post(void *platform, return ret; } -int platform_start_voice_call(void *platform) +int platform_start_voice_call(void *platform, uint32_t vsid __unused) { struct platform_data *my_data = (struct platform_data *)platform; int ret = 0; @@ -502,7 +502,7 @@ int platform_start_voice_call(void *platform) return ret; } -int platform_stop_voice_call(void *platform) +int platform_stop_voice_call(void *platform, uint32_t vsid __unused) { struct platform_data *my_data = (struct platform_data *)platform; int ret = 0; @@ -565,6 +565,12 @@ int platform_set_mic_mute(void *platform, bool state) return ret; } +int platform_set_device_mute(void *platform __unused, bool state __unused, char *dir __unused) +{ + LOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices) { struct platform_data *my_data = (struct platform_data *)platform; @@ -681,10 +687,10 @@ snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_d ALOGE("%s: No output device set for voice call", __func__); goto exit; } - if (adev->tty_mode != TTY_MODE_OFF) { + if (adev->voice.tty_mode != TTY_MODE_OFF) { if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { - switch (adev->tty_mode) { + switch (adev->voice.tty_mode) { case TTY_MODE_FULL: snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC; break; @@ -695,7 +701,7 @@ snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_d snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC; break; default: - ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->tty_mode); + ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice.tty_mode); } goto exit; } @@ -912,6 +918,31 @@ int platform_edid_get_max_channels(void *platform) return max_channels; } +int platform_set_incall_recording_session_id(void *platform __unused, + uint32_t session_id __unused, int rec_mode __unused) +{ + LOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_stop_incall_recording_usecase(void *platform __unused) +{ + LOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_start_incall_music_usecase(void *platform __unused) +{ + LOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_stop_incall_music_usecase(void *platform __unused) +{ + LOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + /* Delay in Us */ int64_t platform_render_latency(audio_usecase_t usecase) { diff --git a/hal/msm8960/platform.h b/hal/msm8960/platform.h index a2ae80f8..4d137aa8 100644 --- a/hal/msm8960/platform.h +++ b/hal/msm8960/platform.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013-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. diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c index f89439ef..5be6a384 100644 --- a/hal/msm8974/platform.c +++ b/hal/msm8974/platform.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013-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. @@ -54,11 +54,7 @@ /* Retry for delay in FW loading*/ #define RETRY_NUMBER 10 #define RETRY_US 500000 - -#define MAX_VOL_INDEX 5 -#define MIN_VOL_INDEX 0 -#define percent_to_index(val, min, max) \ - ((val) * ((max) - (min)) * 0.01 + (min) + .5) +#define MAX_SND_CARD 8 struct audio_block_header { @@ -110,6 +106,16 @@ static const int pcm_device_table[AUDIO_USECASE_MAX][2] = { LOWLATENCY_PCM_DEVICE}, [USECASE_VOICE_CALL] = {VOICE_CALL_PCM_DEVICE, VOICE_CALL_PCM_DEVICE}, + [USECASE_VOICE2_CALL] = {VOICE2_CALL_PCM_DEVICE, VOICE2_CALL_PCM_DEVICE}, + [USECASE_VOLTE_CALL] = {VOLTE_CALL_PCM_DEVICE, VOLTE_CALL_PCM_DEVICE}, + [USECASE_QCHAT_CALL] = {QCHAT_CALL_PCM_DEVICE, QCHAT_CALL_PCM_DEVICE}, + [USECASE_VOWLAN_CALL] = {VOWLAN_CALL_PCM_DEVICE, VOWLAN_CALL_PCM_DEVICE}, + [USECASE_INCALL_REC_UPLINK] = {AUDIO_RECORD_PCM_DEVICE, + AUDIO_RECORD_PCM_DEVICE}, + [USECASE_INCALL_REC_DOWNLINK] = {AUDIO_RECORD_PCM_DEVICE, + AUDIO_RECORD_PCM_DEVICE}, + [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK] = {AUDIO_RECORD_PCM_DEVICE, + AUDIO_RECORD_PCM_DEVICE}, [USECASE_AUDIO_HFP_SCO] = {HFP_PCM_RX, HFP_SCO_RX}, }; @@ -252,24 +258,6 @@ bool is_operator_tmus() return is_tmus; } -static int set_volume_values(int type, int volume, int* values) -{ - values[0] = volume; - values[1] = ALL_SESSION_VSID; - - switch(type) { - case VOLUME_SET: - values[2] = DEFAULT_VOLUME_RAMP_DURATION_MS; - break; - case MUTE_SET: - values[2] = DEFAULT_MUTE_RAMP_DURATION; - break; - default: - return -EINVAL; - } - return 0; -} - static int set_echo_reference(struct mixer *mixer, const char* ec_ref) { struct mixer_ctl *ctl; @@ -454,28 +442,41 @@ void *platform_init(struct audio_device *adev) { char value[PROPERTY_VALUE_MAX]; struct platform_data *my_data; - int retry_num = 0; + int retry_num = 0, snd_card_num = 0; const char *snd_card_name; - adev->mixer = mixer_open(MIXER_CARD); + while (snd_card_num < MAX_SND_CARD) { + adev->mixer = mixer_open(snd_card_num); - while (!adev->mixer && retry_num < RETRY_NUMBER) { - usleep(RETRY_US); - adev->mixer = mixer_open(MIXER_CARD); - retry_num++; - } + while (!adev->mixer && retry_num < RETRY_NUMBER) { + usleep(RETRY_US); + adev->mixer = mixer_open(snd_card_num); + retry_num++; + } - if (!adev->mixer) { - ALOGE("Unable to open the mixer, aborting."); - return NULL; - } + if (!adev->mixer) { + ALOGE("%s: Unable to open the mixer card: %d", __func__, + snd_card_num); + retry_num = 0; + snd_card_num++; + continue; + } - snd_card_name = mixer_get_name(adev->mixer); - ALOGD("%s: snd_card_name: %s", __func__, snd_card_name); + snd_card_name = mixer_get_name(adev->mixer); + ALOGD("%s: snd_card_name: %s", __func__, snd_card_name); - adev->audio_route = audio_route_init(MIXER_CARD, MIXER_XML_PATH); - if (!adev->audio_route) { - ALOGE("%s: Failed to init audio route controls, aborting.", __func__); + adev->audio_route = audio_route_init(snd_card_num, MIXER_XML_PATH); + if (!adev->audio_route) { + ALOGE("%s: Failed to init audio route controls, aborting.", __func__); + return NULL; + } + adev->snd_card = snd_card_num; + ALOGD("%s: Opened sound card:%d", __func__, snd_card_num); + break; + } + + if (snd_card_num >= MAX_SND_CARD) { + ALOGE("%s: Unable to find correct sound card, aborting.", __func__); return NULL; } @@ -580,6 +581,8 @@ void *platform_init(struct audio_device *adev) void platform_deinit(void *platform) { + struct platform_data *my_data = (struct platform_data *)platform; + close_csd_client(my_data->csd); free(platform); } @@ -755,13 +758,13 @@ int platform_switch_voice_call_usecase_route_post(void *platform, return ret; } -int platform_start_voice_call(void *platform) +int platform_start_voice_call(void *platform, uint32_t vsid) { struct platform_data *my_data = (struct platform_data *)platform; int ret = 0; if (my_data->csd != NULL) { - ret = my_data->csd->start_voice(VOICE_VSID); + ret = my_data->csd->start_voice(vsid); if (ret < 0) { ALOGE("%s: csd_start_voice error %d\n", __func__, ret); } @@ -769,13 +772,13 @@ int platform_start_voice_call(void *platform) return ret; } -int platform_stop_voice_call(void *platform) +int platform_stop_voice_call(void *platform, uint32_t vsid) { struct platform_data *my_data = (struct platform_data *)platform; int ret = 0; if (my_data->csd != NULL) { - ret = my_data->csd->stop_voice(VOICE_VSID); + ret = my_data->csd->stop_voice(vsid); if (ret < 0) { ALOGE("%s: csd_stop_voice error %d\n", __func__, ret); } @@ -783,19 +786,36 @@ int platform_stop_voice_call(void *platform) return ret; } +int platform_get_sample_rate(void *platform, uint32_t *rate) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd != NULL) { + ret = my_data->csd->get_sample_rate(rate); + if (ret < 0) { + ALOGE("%s: csd_get_sample_rate error %d\n", __func__, ret); + } + } + return ret; +} + int platform_set_voice_volume(void *platform, int volume) { struct platform_data *my_data = (struct platform_data *)platform; struct audio_device *adev = my_data->adev; struct mixer_ctl *ctl; const char *mixer_ctl_name = "Voice Rx Gain"; - int values[VOLUME_CTL_PARAM_NUM]; - int ret = 0; + int vol_index = 0, ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID, + DEFAULT_VOLUME_RAMP_DURATION_MS}; // Voice volume levels are mapped to adsp volume levels as follows. // 100 -> 5, 80 -> 4, 60 -> 3, 40 -> 2, 20 -> 1 0 -> 0 // But this values don't changed in kernel. So, below change is need. - volume = (int)percent_to_index(volume, MIN_VOL_INDEX, MAX_VOL_INDEX); + vol_index = (int)percent_to_index(volume, MIN_VOL_INDEX, MAX_VOL_INDEX); + set_values[0] = vol_index; ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); if (!ctl) { @@ -803,16 +823,9 @@ int platform_set_voice_volume(void *platform, int volume) __func__, mixer_ctl_name); return -EINVAL; } - ret = set_volume_values(VOLUME_SET, volume, values); - if (ret < 0) { - ALOGV("%s: failed setting volume by incorrect type", __func__); - return -EINVAL; - } - ret = mixer_ctl_set_array(ctl, values, sizeof(values)/sizeof(int)); - if (ret < 0) { - ALOGV("%s: failed set mixer ctl by %d", __func__, ret); - return -EINVAL; - } + ALOGV("Setting voice volume index: %d", set_values[0]); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + if (my_data->csd != NULL) { ret = my_data->csd->volume(ALL_SESSION_VSID, volume, DEFAULT_VOLUME_RAMP_DURATION_MS); @@ -829,30 +842,70 @@ int platform_set_mic_mute(void *platform, bool state) struct audio_device *adev = my_data->adev; struct mixer_ctl *ctl; const char *mixer_ctl_name = "Voice Tx Mute"; - int values[VOLUME_CTL_PARAM_NUM]; int ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID, + DEFAULT_MUTE_RAMP_DURATION_MS}; - if (adev->mode == AUDIO_MODE_IN_CALL) { - 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; - } - ALOGV("Setting mic mute: %d", state); - ret = set_volume_values(MUTE_SET, state, values); - if (ret < 0) { - ALOGV("%s: failed setting mute by incorrect type", __func__); - return -EINVAL; - } - ret = mixer_ctl_set_array(ctl, values, sizeof(values)/sizeof(int)); + if (adev->mode != AUDIO_MODE_IN_CALL) + return 0; + + set_values[0] = state; + 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; + } + ALOGV("Setting voice mute state: %d", state); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + if (my_data->csd != NULL) { + ret = my_data->csd->mic_mute(ALL_SESSION_VSID, state, + DEFAULT_MUTE_RAMP_DURATION_MS); if (ret < 0) { - ALOGV("%s: failed set mixer ctl by %d", __func__, ret); - return -EINVAL; + ALOGE("%s: csd_mic_mute error %d", __func__, ret); } } + return ret; +} - return 0; +int platform_set_device_mute(void *platform, bool state, char *dir) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + char *mixer_ctl_name = NULL; + int ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID, + 0}; + if(dir == NULL) { + ALOGE("%s: Invalid direction:%s", __func__, dir); + return -EINVAL; + } + + if (!strncmp("rx", dir, sizeof("rx"))) { + mixer_ctl_name = "Voice Rx Device Mute"; + } else if (!strncmp("tx", dir, sizeof("tx"))) { + mixer_ctl_name = "Voice Tx Device Mute"; + } else { + return -EINVAL; + } + + set_values[0] = state; + 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; + } + + ALOGV("%s: Setting device mute state: %d, mixer ctrl:%s", + __func__,state, mixer_ctl_name); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + return ret; } snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices) @@ -872,11 +925,11 @@ snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devi if (mode == AUDIO_MODE_IN_CALL) { if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) { - if (adev->tty_mode == TTY_MODE_FULL) + if (adev->voice.tty_mode == TTY_MODE_FULL) snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES; - else if (adev->tty_mode == TTY_MODE_VCO) + else if (adev->voice.tty_mode == TTY_MODE_VCO) snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES; - else if (adev->tty_mode == TTY_MODE_HCO) + else if (adev->voice.tty_mode == TTY_MODE_HCO) snd_device = SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET; else snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES; @@ -971,10 +1024,10 @@ snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_d ALOGE("%s: No output device set for voice call", __func__); goto exit; } - if (adev->tty_mode != TTY_MODE_OFF) { + if (adev->voice.tty_mode != TTY_MODE_OFF) { if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { - switch (adev->tty_mode) { + switch (adev->voice.tty_mode) { case TTY_MODE_FULL: snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC; break; @@ -985,7 +1038,7 @@ snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_d snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC; break; default: - ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->tty_mode); + ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice.tty_mode); } goto exit; } @@ -1212,6 +1265,92 @@ int platform_edid_get_max_channels(void *platform) return max_channels; } +int platform_set_incall_recording_session_id(void *platform, + uint32_t session_id, int rec_mode) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voc VSID"; + int num_ctl_values; + int i; + + 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); + ret = -EINVAL; + } else { + num_ctl_values = mixer_ctl_get_num_values(ctl); + for (i = 0; i < num_ctl_values; i++) { + if (mixer_ctl_set_value(ctl, i, session_id)) { + ALOGV("Error: invalid session_id: %x", session_id); + ret = -EINVAL; + break; + } + } + } + + if (my_data->csd != NULL) { + ret = my_data->csd->start_record(ALL_SESSION_VSID, rec_mode); + if (ret < 0) { + ALOGE("%s: csd_client_start_record failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_stop_incall_recording_usecase(void *platform) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->csd != NULL) { + ret = my_data->csd->stop_record(ALL_SESSION_VSID); + if (ret < 0) { + ALOGE("%s: csd_client_stop_record failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_start_incall_music_usecase(void *platform) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->csd != NULL) { + ret = my_data->csd->start_playback(ALL_SESSION_VSID); + if (ret < 0) { + ALOGE("%s: csd_client_start_playback failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_stop_incall_music_usecase(void *platform) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->csd != NULL) { + ret = my_data->csd->stop_playback(ALL_SESSION_VSID); + if (ret < 0) { + ALOGE("%s: csd_client_stop_playback failed, error %d", + __func__, ret); + } + } + + return ret; +} + /* Delay in Us */ int64_t platform_render_latency(audio_usecase_t usecase) { diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h index 42bf8e5d..267f90f8 100644 --- a/hal/msm8974/platform.h +++ b/hal/msm8974/platform.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013-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. @@ -91,17 +91,11 @@ enum { }; -#define MIXER_CARD 0 -#define SOUND_CARD 0 - #define DEFAULT_OUTPUT_SAMPLING_RATE 48000 -#define ALL_SESSION_VSID 0xFFFFFFFF -#define DEFAULT_MUTE_RAMP_DURATION 20 +#define ALL_SESSION_VSID 0xFFFFFFFF +#define DEFAULT_MUTE_RAMP_DURATION_MS 20 #define DEFAULT_VOLUME_RAMP_DURATION_MS 20 -#define VOLUME_SET 0 -#define MUTE_SET 1 -#define VOLUME_CTL_PARAM_NUM 3 #ifdef MSM8084 #define ACDB_ID_VOICE_HANDSET_TMUS 88 @@ -110,6 +104,12 @@ enum { #define ACDB_ID_VOICE_HANDSET_TMUS 7 #define ACDB_ID_VOICE_DMIC_EF_TMUS 41 #endif + +#define MAX_VOL_INDEX 5 +#define MIN_VOL_INDEX 0 +#define percent_to_index(val, min, max) \ + ((val) * ((max) - (min)) * 0.01 + (min) + .5) + /* * tinyAlsa library interprets period size as number of frames * one frame = channel_count * sizeof (pcm sample) @@ -143,8 +143,16 @@ enum { #define VOICE_VSID 0x10C01000 #ifdef PLATFORM_MSM8084 #define VOICE_CALL_PCM_DEVICE 20 +#define VOICE2_CALL_PCM_DEVICE 25 +#define VOLTE_CALL_PCM_DEVICE 21 +#define QCHAT_CALL_PCM_DEVICE 33 +#define VOWLAN_CALL_PCM_DEVICE -1 #else #define VOICE_CALL_PCM_DEVICE 2 +#define VOICE2_CALL_PCM_DEVICE 22 +#define VOLTE_CALL_PCM_DEVICE 14 +#define QCHAT_CALL_PCM_DEVICE 20 +#define VOWLAN_CALL_PCM_DEVICE 36 #endif #define HFP_PCM_RX 5 diff --git a/hal/platform_api.h b/hal/platform_api.h index 4a306d57..81fbc08e 100644 --- a/hal/platform_api.h +++ b/hal/platform_api.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013-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. @@ -34,10 +34,12 @@ int platform_switch_voice_call_device_post(void *platform, int platform_switch_voice_call_usecase_route_post(void *platform, snd_device_t out_snd_device, snd_device_t in_snd_device); -int platform_start_voice_call(void *platform); -int platform_stop_voice_call(void *platform); +int platform_start_voice_call(void *platform, uint32_t vsid); +int platform_stop_voice_call(void *platform, uint32_t vsid); int platform_set_voice_volume(void *platform, int volume); int platform_set_mic_mute(void *platform, bool state); +int platform_get_sample_rate(void *platform, uint32_t *rate); +int platform_set_device_mute(void *platform, bool state, char *dir); snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices); snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_device); int platform_set_hdmi_channels(void *platform, int channel_count); @@ -46,4 +48,10 @@ int platform_edid_get_max_channels(void *platform); /* returns the latency for a usecase in Us */ int64_t platform_render_latency(audio_usecase_t usecase); +int platform_set_incall_recording_session_id(void *platform, + uint32_t session_id, int rec_mode); +int platform_stop_incall_recording_usecase(void *platform); +int platform_start_incall_music_usecase(void *platform); +int platform_stop_incall_music_usecase(void *platform); + #endif // QCOM_AUDIO_PLATFORM_API_H diff --git a/hal/voice.c b/hal/voice.c new file mode 100644 index 00000000..cc509af2 --- /dev/null +++ b/hal/voice.c @@ -0,0 +1,451 @@ +/* + * 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 "voice" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include <errno.h> +#include <math.h> +#include <cutils/log.h> +#include <cutils/str_parms.h> + +#include "audio_hw.h" +#include "voice.h" +#include "voice_extn/voice_extn.h" +#include "platform.h" +#include "platform_api.h" + +struct pcm_config pcm_config_voice_call = { + .channels = 1, + .rate = 8000, + .period_size = 160, + .period_count = 2, + .format = PCM_FORMAT_S16_LE, +}; + +extern const char * const use_case_table[AUDIO_USECASE_MAX]; + +static struct voice_session *voice_get_session_from_use_case(struct audio_device *adev, + audio_usecase_t usecase_id) +{ + struct voice_session *session = NULL; + int ret = 0; + + ret = voice_extn_get_session_from_use_case(adev, usecase_id, &session); + if (ret == -ENOSYS) { + session = &adev->voice.session[VOICE_SESS_IDX]; + } + + return session; +} + +int stop_call(struct audio_device *adev, audio_usecase_t usecase_id) +{ + int i, ret = 0; + struct audio_usecase *uc_info; + struct voice_session *session = NULL; + + ALOGD("%s: enter usecase:%s", __func__, use_case_table[usecase_id]); + + session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id); + session->state.current = CALL_INACTIVE; + + ret = platform_stop_voice_call(adev->platform, session->vsid); + + /* 1. Close the PCM devices */ + if (session->pcm_rx) { + pcm_close(session->pcm_rx); + session->pcm_rx = NULL; + } + if (session->pcm_tx) { + pcm_close(session->pcm_tx); + session->pcm_tx = NULL; + } + + uc_info = get_usecase_from_list(adev, usecase_id); + if (uc_info == NULL) { + ALOGE("%s: Could not find the usecase (%d) in the list", + __func__, usecase_id); + 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); + + list_remove(&uc_info->list); + free(uc_info); + + ALOGD("%s: exit: status(%d)", __func__, ret); + return ret; +} + +int start_call(struct audio_device *adev, audio_usecase_t usecase_id) +{ + int i, ret = 0; + struct audio_usecase *uc_info; + int pcm_dev_rx_id, pcm_dev_tx_id; + uint32_t sample_rate = 8000; + struct voice_session *session = NULL; + struct pcm_config voice_config = pcm_config_voice_call; + + ALOGD("%s: enter usecase:%s", __func__, use_case_table[usecase_id]); + + session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id); + uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); + uc_info->id = usecase_id; + uc_info->type = VOICE_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); + + select_devices(adev, usecase_id); + + 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); + + if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) { + ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)", + __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id); + ret = -EIO; + goto error_start_voice; + } + ret = platform_get_sample_rate(adev->platform, &sample_rate); + if (ret < 0) { + ALOGE("platform_get_sample_rate error %d\n", ret); + } else { + voice_config.rate = sample_rate; + } + ALOGD("voice_config.rate %d\n", voice_config.rate); + + ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_rx_id); + session->pcm_rx = pcm_open(adev->snd_card, + pcm_dev_rx_id, + PCM_OUT, &voice_config); + if (session->pcm_rx && !pcm_is_ready(session->pcm_rx)) { + ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_rx)); + ret = -EIO; + goto error_start_voice; + } + + ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_tx_id); + session->pcm_tx = pcm_open(adev->snd_card, + pcm_dev_tx_id, + PCM_IN, &voice_config); + if (session->pcm_tx && !pcm_is_ready(session->pcm_tx)) { + ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_tx)); + ret = -EIO; + goto error_start_voice; + } + pcm_start(session->pcm_rx); + pcm_start(session->pcm_tx); + + voice_set_volume(adev, adev->voice.volume); + + ret = platform_start_voice_call(adev->platform, session->vsid); + if (ret < 0) { + ALOGE("%s: platform_start_voice_call error %d\n", __func__, ret); + goto error_start_voice; + } + + session->state.current = CALL_ACTIVE; + return 0; + +error_start_voice: + stop_call(adev, usecase_id); + + ALOGD("%s: exit: status(%d)", __func__, ret); + return ret; +} + +bool voice_is_in_call(struct audio_device *adev) +{ + bool in_call = false; + int ret = 0; + + ret = voice_extn_is_in_call(adev, &in_call); + if (ret == -ENOSYS) { + in_call = (adev->voice.session[VOICE_SESS_IDX].state.current == CALL_ACTIVE) ? true : false; + } + + return in_call; +} + +bool voice_is_in_call_rec_stream(struct stream_in *in) +{ + bool in_call_rec = false; + int ret = 0; + + ret = voice_extn_is_in_call_rec_stream(in, &in_call_rec); + if (ret == -ENOSYS) { + in_call_rec = false; + } + + return in_call_rec; +} + +uint32_t voice_get_active_session_id(struct audio_device *adev) +{ + int ret = 0; + uint32_t session_id; + + ret = voice_extn_get_active_session_id(adev, &session_id); + if (ret == -ENOSYS) { + session_id = VOICE_VSID; + } + return session_id; +} + +int voice_check_and_set_incall_rec_usecase(struct audio_device *adev, + struct stream_in *in) +{ + int ret = 0; + uint32_t session_id; + int usecase_id; + int rec_mode = INCALL_REC_NONE; + + if (voice_is_in_call(adev)) { + switch (in->source) { + case AUDIO_SOURCE_VOICE_UPLINK: + in->usecase = USECASE_INCALL_REC_UPLINK; + rec_mode = INCALL_REC_UPLINK; + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + in->usecase = USECASE_INCALL_REC_DOWNLINK; + rec_mode = INCALL_REC_DOWNLINK; + break; + case AUDIO_SOURCE_VOICE_CALL: + in->usecase = USECASE_INCALL_REC_UPLINK_AND_DOWNLINK; + rec_mode = INCALL_REC_UPLINK_AND_DOWNLINK; + break; + default: + ALOGV("%s: Source type %d doesnt match incall recording criteria", + __func__, in->source); + return ret; + } + + session_id = voice_get_active_session_id(adev); + ret = platform_set_incall_recording_session_id(adev->platform, + session_id, rec_mode); + ALOGV("%s: Update usecase to %d",__func__, in->usecase); + } else { + ALOGV("%s: voice call not active", __func__); + } + + return ret; +} + +int voice_check_and_stop_incall_rec_usecase(struct audio_device *adev, + struct stream_in *in) +{ + int ret = 0; + + if (in->source == AUDIO_SOURCE_VOICE_UPLINK || + in->source == AUDIO_SOURCE_VOICE_DOWNLINK || + in->source == AUDIO_SOURCE_VOICE_CALL) { + ret = platform_stop_incall_recording_usecase(adev->platform); + ALOGV("%s: Stop In-call recording", __func__); + } + + return ret; +} + +int voice_check_and_set_incall_music_usecase(struct audio_device *adev, + struct stream_out *out) +{ + int ret = 0; + + ret = voice_extn_check_and_set_incall_music_usecase(adev, out); + if (ret == -ENOSYS) { + /* Incall music delivery is used only for LCH call state */ + ret = -EINVAL; + } + + return ret; +} + +int voice_set_mic_mute(struct audio_device *adev, bool state) +{ + int err = 0; + + adev->voice.mic_mute = state; + if (adev->mode == AUDIO_MODE_IN_CALL) + err = platform_set_mic_mute(adev->platform, state); + + return err; +} + +bool voice_get_mic_mute(struct audio_device *adev) +{ + return adev->voice.mic_mute; +} + +int voice_set_volume(struct audio_device *adev, float volume) +{ + int vol, err = 0; + + adev->voice.volume = volume; + if (adev->mode == AUDIO_MODE_IN_CALL) { + if (volume < 0.0) { + volume = 0.0; + } else if (volume > 1.0) { + volume = 1.0; + } + + vol = lrint(volume * 100.0); + + // Voice volume levels from android are mapped to driver volume levels as follows. + // 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0 + // So adjust the volume to get the correct volume index in driver + vol = 100 - vol; + + err = platform_set_voice_volume(adev->platform, vol); + } + + return err; +} + +int voice_start_call(struct audio_device *adev) +{ + int ret = 0; + + ret = voice_extn_start_call(adev); + if (ret == -ENOSYS) { + ret = start_call(adev, USECASE_VOICE_CALL); + } + + return ret; +} + +int voice_stop_call(struct audio_device *adev) +{ + int ret = 0; + + ret = voice_extn_stop_call(adev); + if (ret == -ENOSYS) { + ret = stop_call(adev, USECASE_VOICE_CALL); + } + + return ret; +} + +void voice_get_parameters(struct audio_device *adev, + struct str_parms *query, + struct str_parms *reply) +{ + voice_extn_get_parameters(adev, query, reply); +} + +int voice_set_parameters(struct audio_device *adev, struct str_parms *parms) +{ + char *str; + char value[32]; + int val; + int ret = 0, err; + char *kv_pairs = str_parms_to_str(parms); + + ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs); + + ret = voice_extn_set_parameters(adev, parms); + if (ret != 0) + goto done; + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value)); + if (err >= 0) { + int tty_mode; + str_parms_del(parms, AUDIO_PARAMETER_KEY_TTY_MODE); + if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0) + tty_mode = TTY_MODE_OFF; + else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0) + tty_mode = TTY_MODE_VCO; + else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0) + tty_mode = TTY_MODE_HCO; + else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0) + tty_mode = TTY_MODE_FULL; + else { + ret = -EINVAL; + goto done; + } + + if (tty_mode != adev->voice.tty_mode) { + adev->voice.tty_mode = tty_mode; + adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode; + if (voice_is_in_call(adev)) + voice_update_devices_for_all_voice_usecases(adev); + } + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_INCALLMUSIC, + value, sizeof(value)); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_INCALLMUSIC); + if (strcmp(value, AUDIO_PARAMETER_VALUE_TRUE) == 0) + platform_start_incall_music_usecase(adev->platform); + else + platform_stop_incall_music_usecase(adev->platform); + } + +done: + ALOGV("%s: exit with code(%d)", __func__, ret); + free(kv_pairs); + return ret; +} + +void voice_init(struct audio_device *adev) +{ + int i = 0; + + memset(&adev->voice, 0, sizeof(adev->voice)); + adev->voice.tty_mode = TTY_MODE_OFF; + adev->voice.volume = 1.0f; + adev->voice.mic_mute = false; + adev->voice.voice_device_set = false; + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + adev->voice.session[i].pcm_rx = NULL; + adev->voice.session[i].pcm_tx = NULL; + adev->voice.session[i].state.current = CALL_INACTIVE; + adev->voice.session[i].state.new = CALL_INACTIVE; + adev->voice.session[i].vsid = VOICE_VSID; + } + + voice_extn_init(adev); +} + +void voice_update_devices_for_all_voice_usecases(struct audio_device *adev) +{ + struct listnode *node; + struct audio_usecase *usecase; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type == VOICE_CALL) { + ALOGV("%s: updating device for usecase:%s", __func__, + use_case_table[usecase->id]); + select_devices(adev, usecase->id); + } + } +} + + diff --git a/hal/voice.h b/hal/voice.h new file mode 100644 index 00000000..ba2240c4 --- /dev/null +++ b/hal/voice.h @@ -0,0 +1,88 @@ +/* + * 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 VOICE_H +#define VOICE_H + +#define BASE_SESS_IDX 0 +#define VOICE_SESS_IDX (BASE_SESS_IDX) + +#ifdef MULTI_VOICE_SESSION_ENABLED +#define MAX_VOICE_SESSIONS 5 +#else +#define MAX_VOICE_SESSIONS 1 +#endif + +#define BASE_CALL_STATE 1 +#define CALL_INACTIVE (BASE_CALL_STATE) +#define CALL_ACTIVE (BASE_CALL_STATE + 1) + +#define VOICE_VSID 0x10C01000 + +#define AUDIO_PARAMETER_KEY_INCALLMUSIC "incall_music_enabled" +#define AUDIO_PARAMETER_VALUE_TRUE "true" + +struct audio_device; +struct str_parms; +struct stream_in; +struct stream_out; + +struct call_state { + int current; + int new; +}; + +struct voice_session { + struct pcm *pcm_rx; + struct pcm *pcm_tx; + struct call_state state; + uint32_t vsid; +}; + +struct voice { + struct voice_session session[MAX_VOICE_SESSIONS]; + int tty_mode; + bool mic_mute; + float volume; + bool voice_device_set; +}; + +enum { + INCALL_REC_NONE = -1, + INCALL_REC_UPLINK, + INCALL_REC_DOWNLINK, + INCALL_REC_UPLINK_AND_DOWNLINK, +}; + +int voice_start_call(struct audio_device *adev); +int voice_stop_call(struct audio_device *adev); +int voice_set_parameters(struct audio_device *adev, struct str_parms *parms); +void voice_get_parameters(struct audio_device *adev, struct str_parms *query, + struct str_parms *reply); +void voice_init(struct audio_device *adev); +bool voice_is_in_call(struct audio_device *adev); +bool voice_is_in_call_rec_stream(struct stream_in *in); +int voice_set_mic_mute(struct audio_device *dev, bool state); +bool voice_get_mic_mute(struct audio_device *dev); +int voice_set_volume(struct audio_device *adev, float volume); +int voice_check_and_set_incall_rec_usecase(struct audio_device *adev, + struct stream_in *in); +int voice_check_and_set_incall_music_usecase(struct audio_device *adev, + struct stream_out *out); +int voice_check_and_stop_incall_rec_usecase(struct audio_device *adev, + struct stream_in *in); +void voice_update_devices_for_all_voice_usecases(struct audio_device *adev); +#endif //VOICE_H diff --git a/hal/voice_extn/voice_extn.c b/hal/voice_extn/voice_extn.c new file mode 100644 index 00000000..05c5fcbd --- /dev/null +++ b/hal/voice_extn/voice_extn.c @@ -0,0 +1,585 @@ +/* + * 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 "voice_extn" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include <errno.h> +#include <math.h> +#include <cutils/log.h> +#include <cutils/str_parms.h> +#include <sys/ioctl.h> +#include <sound/voice_params.h> + +#include "audio_hw.h" +#include "voice.h" +#include "platform.h" +#include "platform_api.h" +#include "voice_extn.h" + +#define AUDIO_PARAMETER_KEY_VSID "vsid" +#define AUDIO_PARAMETER_KEY_CALL_STATE "call_state" +#define AUDIO_PARAMETER_KEY_AUDIO_MODE "audio_mode" +#define AUDIO_PARAMETER_KEY_ALL_CALL_STATES "all_call_states" +#define AUDIO_PARAMETER_KEY_DEVICE_MUTE "device_mute" +#define AUDIO_PARAMETER_KEY_DIRECTION "direction" + +#define VOICE_EXTN_PARAMETER_VALUE_MAX_LEN 256 + +#define VOICE2_VSID 0x10DC1000 +#define VOLTE_VSID 0x10C02000 +#define QCHAT_VSID 0x10803000 +#define VOWLAN_VSID 0x10002000 +#define ALL_VSID 0xFFFFFFFF + +/* Voice Session Indices */ +#define VOICE2_SESS_IDX (VOICE_SESS_IDX + 1) +#define VOLTE_SESS_IDX (VOICE_SESS_IDX + 2) +#define QCHAT_SESS_IDX (VOICE_SESS_IDX + 3) +#define VOWLAN_SESS_IDX (VOICE_SESS_IDX + 4) + +/* Call States */ +#define CALL_HOLD (BASE_CALL_STATE + 2) +#define CALL_LOCAL_HOLD (BASE_CALL_STATE + 3) + +struct pcm_config pcm_config_incall_music = { + .channels = 1, + .rate = DEFAULT_OUTPUT_SAMPLING_RATE, + .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE, + .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, + .stop_threshold = INT_MAX, + .avail_min = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, +}; + +extern int start_call(struct audio_device *adev, audio_usecase_t usecase_id); +extern int stop_call(struct audio_device *adev, audio_usecase_t usecase_id); +int voice_extn_is_in_call(struct audio_device *adev, bool *in_call); + +static bool is_valid_call_state(int call_state) +{ + if (call_state < CALL_INACTIVE || call_state > CALL_LOCAL_HOLD) + return false; + else + return true; +} + +static bool is_valid_vsid(uint32_t vsid) +{ + if (vsid == VOICE_VSID || + vsid == VOICE2_VSID || + vsid == VOLTE_VSID || + vsid == QCHAT_VSID || + vsid == VOWLAN_VSID) + return true; + else + return false; +} + +static audio_usecase_t voice_extn_get_usecase_for_session_idx(const int index) +{ + audio_usecase_t usecase_id = -1; + + switch(index) { + case VOICE_SESS_IDX: + usecase_id = USECASE_VOICE_CALL; + break; + + case VOICE2_SESS_IDX: + usecase_id = USECASE_VOICE2_CALL; + break; + + case VOLTE_SESS_IDX: + usecase_id = USECASE_VOLTE_CALL; + break; + + case QCHAT_SESS_IDX: + usecase_id = USECASE_QCHAT_CALL; + break; + + case VOWLAN_SESS_IDX: + usecase_id = USECASE_VOWLAN_CALL; + break; + + default: + ALOGE("%s: Invalid voice session index\n", __func__); + } + + return usecase_id; +} + +static uint32_t get_session_id_with_state(struct audio_device *adev, + int call_state) +{ + struct voice_session *session = NULL; + int i = 0; + uint32_t session_id = 0; + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + session = &adev->voice.session[i]; + if(session->state.current == call_state){ + session_id = session->vsid; + break; + } + } + + return session_id; +} + +static int update_calls(struct audio_device *adev) +{ + int i = 0; + audio_usecase_t usecase_id = 0; + enum voice_lch_mode lch_mode; + struct voice_session *session = NULL; + int fd = 0; + int ret = 0; + bool is_in_call = false; + + ALOGD("%s: enter:", __func__); + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + usecase_id = voice_extn_get_usecase_for_session_idx(i); + session = &adev->voice.session[i]; + ALOGD("%s: cur_state=%d new_state=%d vsid=%x", + __func__, session->state.current, session->state.new, session->vsid); + + switch(session->state.new) + { + case CALL_ACTIVE: + switch(session->state.current) + { + case CALL_INACTIVE: + ALOGD("%s: INACTIVE -> ACTIVE vsid:%x", __func__, session->vsid); + ret = start_call(adev, usecase_id); + if(ret < 0) { + ALOGE("%s: voice_start_call() failed for usecase: %d\n", + __func__, usecase_id); + } else { + session->state.current = session->state.new; + } + break; + + case CALL_HOLD: + ALOGD("%s: HOLD -> ACTIVE vsid:%x", __func__, session->vsid); + session->state.current = session->state.new; + break; + + case CALL_LOCAL_HOLD: + ALOGD("%s: LOCAL_HOLD -> ACTIVE vsid:%x", __func__, session->vsid); + lch_mode = VOICE_LCH_STOP; + if (pcm_ioctl(session->pcm_tx, SNDRV_VOICE_IOCTL_LCH, &lch_mode) < 0) { + ALOGE("LOCAL_HOLD -> ACTIVE failed"); + } else { + session->state.current = session->state.new; + } + break; + + default: + ALOGV("%s: CALL_ACTIVE cannot be handled in state=%d vsid:%x", + __func__, session->state.current, session->vsid); + break; + } + break; + + case CALL_INACTIVE: + switch(session->state.current) + { + case CALL_ACTIVE: + case CALL_HOLD: + case CALL_LOCAL_HOLD: + ALOGD("%s: ACTIVE/HOLD/LOCAL_HOLD -> INACTIVE vsid:%x", __func__, session->vsid); + ret = stop_call(adev, usecase_id); + if(ret < 0) { + ALOGE("%s: voice_end_call() failed for usecase: %d\n", + __func__, usecase_id); + } else { + voice_extn_is_in_call(adev, &is_in_call); + if (!is_in_call) { + adev->voice.voice_device_set = false; + } + session->state.current = session->state.new; + } + break; + + default: + ALOGV("%s: CALL_INACTIVE cannot be handled in state=%d vsid:%x", + __func__, session->state.current, session->vsid); + break; + } + break; + + case CALL_HOLD: + switch(session->state.current) + { + case CALL_ACTIVE: + ALOGD("%s: CALL_ACTIVE -> HOLD vsid:%x", __func__, session->vsid); + session->state.current = session->state.new; + break; + + case CALL_LOCAL_HOLD: + ALOGD("%s: CALL_LOCAL_HOLD -> HOLD vsid:%x", __func__, session->vsid); + lch_mode = VOICE_LCH_STOP; + if (pcm_ioctl(session->pcm_tx, SNDRV_VOICE_IOCTL_LCH, &lch_mode) < 0) { + ALOGE("LOCAL_HOLD -> HOLD failed"); + } else { + session->state.current = session->state.new; + } + break; + + default: + ALOGV("%s: CALL_HOLD cannot be handled in state=%d vsid:%x", + __func__, session->state.current, session->vsid); + break; + } + break; + + case CALL_LOCAL_HOLD: + switch(session->state.current) + { + case CALL_ACTIVE: + case CALL_HOLD: + ALOGD("%s: ACTIVE/CALL_HOLD -> LOCAL_HOLD vsid:%x", __func__, + session->vsid); + lch_mode = VOICE_LCH_START; + if (pcm_ioctl(session->pcm_tx, SNDRV_VOICE_IOCTL_LCH, &lch_mode) < 0) { + ALOGE("LOCAL_HOLD -> HOLD failed"); + } else { + session->state.current = session->state.new; + } + break; + + default: + ALOGV("%s: CALL_LOCAL_HOLD cannot be handled in state=%d vsid:%x", + __func__, session->state.current, session->vsid); + break; + } + break; + + default: + break; + } //end out switch loop + } //end for loop + + return ret; +} + +static int update_call_states(struct audio_device *adev, + const uint32_t vsid, const int call_state) +{ + struct voice_session *session = NULL; + int i = 0; + bool is_in_call; + int no_of_calls_active = 0; + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + if (vsid == adev->voice.session[i].vsid) { + session = &adev->voice.session[i]; + break; + } + } + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + if (CALL_INACTIVE != adev->voice.session[i].state.current) + no_of_calls_active++; + } + + if (session) { + session->state.new = call_state; + voice_extn_is_in_call(adev, &is_in_call); + ALOGD("%s is_in_call:%d voice_device_set:%d, mode:%d\n", + __func__, is_in_call, adev->voice.voice_device_set, adev->mode); + /* Dont start voice call before device routing for voice usescases has + * occured, otherwise voice calls will be started unintendedly on + * speaker. + */ + if (is_in_call || adev->voice.voice_device_set) { + /* Device routing is not triggered for voice calls on the subsequent + * subs, Hence update the call states if voice call is already + * active on other sub. + */ + update_calls(adev); + } + } else { + return -EINVAL; + } + + return 0; + +} + +int voice_extn_get_active_session_id(struct audio_device *adev, + uint32_t *session_id) +{ + *session_id = get_session_id_with_state(adev, CALL_ACTIVE); + return 0; +} + +int voice_extn_is_in_call(struct audio_device *adev, bool *in_call) +{ + struct voice_session *session = NULL; + int i = 0; + *in_call = false; + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + session = &adev->voice.session[i]; + if(session->state.current != CALL_INACTIVE){ + *in_call = true; + break; + } + } + + return 0; +} + +int voice_extn_is_in_call_rec_stream(struct stream_in *in, bool *in_call_rec) +{ + *in_call_rec = false; + + if(in->source == AUDIO_SOURCE_VOICE_DOWNLINK || + in->source == AUDIO_SOURCE_VOICE_UPLINK || + in->source == AUDIO_SOURCE_VOICE_CALL) { + *in_call_rec = true; + } + + return 0; +} + +void voice_extn_init(struct audio_device *adev) +{ + adev->voice.session[VOICE_SESS_IDX].vsid = VOICE_VSID; + adev->voice.session[VOICE2_SESS_IDX].vsid = VOICE2_VSID; + adev->voice.session[VOLTE_SESS_IDX].vsid = VOLTE_VSID; + adev->voice.session[QCHAT_SESS_IDX].vsid = QCHAT_VSID; + adev->voice.session[VOWLAN_SESS_IDX].vsid = VOWLAN_VSID; +} + +int voice_extn_get_session_from_use_case(struct audio_device *adev, + const audio_usecase_t usecase_id, + struct voice_session **session) +{ + + switch(usecase_id) + { + case USECASE_VOICE_CALL: + *session = &adev->voice.session[VOICE_SESS_IDX]; + break; + + case USECASE_VOICE2_CALL: + *session = &adev->voice.session[VOICE2_SESS_IDX]; + break; + + case USECASE_VOLTE_CALL: + *session = &adev->voice.session[VOLTE_SESS_IDX]; + break; + + case USECASE_QCHAT_CALL: + *session = &adev->voice.session[QCHAT_SESS_IDX]; + break; + + case USECASE_VOWLAN_CALL: + *session = &adev->voice.session[VOWLAN_SESS_IDX]; + break; + + default: + ALOGE("%s: Invalid usecase_id:%d\n", __func__, usecase_id); + *session = NULL; + return -EINVAL; + } + + return 0; +} + +int voice_extn_start_call(struct audio_device *adev) +{ + /* Start voice calls on sessions whose call state has been + * udpated. + */ + ALOGV("%s: enter:", __func__); + adev->voice.voice_device_set = true; + return update_calls(adev); +} + +int voice_extn_stop_call(struct audio_device *adev) +{ + int i; + int ret = 0; + + ALOGV("%s: enter:", __func__); + + /* If BT device is enabled and voice calls are ended, telephony will call + * set_mode(AUDIO_MODE_NORMAL) which will trigger audio policy manager to + * set routing with device BT A2DP profile. Hence end all voice calls when + * set_mode(AUDIO_MODE_NORMAL) before BT A2DP profile is selected. + */ + if (adev->mode == AUDIO_MODE_NORMAL) { + ALOGD("%s: end all calls", __func__); + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + adev->voice.session[i].state.new = CALL_INACTIVE; + } + + ret = update_calls(adev); + } + + return ret; +} + +int voice_extn_set_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + char *str; + int value; + int ret = 0, err; + char *kv_pairs = str_parms_to_str(parms); + char str_value[256] = {0}; + + ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs); + + err = str_parms_get_int(parms, AUDIO_PARAMETER_KEY_VSID, &value); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_VSID); + uint32_t vsid = value; + int call_state = -1; + err = str_parms_get_int(parms, AUDIO_PARAMETER_KEY_CALL_STATE, &value); + if (err >= 0) { + call_state = value; + } else { + ALOGE("%s: call_state key not found", __func__); + ret = -EINVAL; + goto done; + } + + if (is_valid_vsid(vsid) && is_valid_call_state(call_state)) { + ret = update_call_states(adev, vsid, call_state); + } else { + ALOGE("%s: invalid vsid:%x or call_state:%d", + __func__, vsid, call_state); + ret = -EINVAL; + goto done; + } + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DEVICE_MUTE, str_value, + sizeof(str_value)); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_DEVICE_MUTE); + bool mute = false; + + if (!strncmp("true", str_value, sizeof("true"))) { + mute = true; + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DIRECTION, str_value, + sizeof(str_value)); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_DIRECTION); + } else { + ALOGE("%s: direction key not found", __func__); + ret = -EINVAL; + goto done; + } + + ret = platform_set_device_mute(adev->platform, mute, str_value); + if (ret != 0) { + ALOGE("%s: Failed to set mute err:%d", __func__, ret); + ret = -EINVAL; + goto done; + } + } + +done: + ALOGV("%s: exit with code(%d)", __func__, ret); + free(kv_pairs); + return ret; +} + +static int get_all_call_states_str(const struct audio_device *adev, + char *value) +{ + int ret = 0; + char *cur_ptr = value; + int i, len=0; + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + snprintf(cur_ptr, VOICE_EXTN_PARAMETER_VALUE_MAX_LEN - len, + "%d:%d,",adev->voice.session[i].vsid, + adev->voice.session[i].state.current); + len = strlen(cur_ptr); + cur_ptr = cur_ptr + len; + } + ALOGV("%s:value=%s", __func__, value); + return ret; +} + +void voice_extn_get_parameters(const struct audio_device *adev, + struct str_parms *query, + struct str_parms *reply) +{ + int ret; + char value[VOICE_EXTN_PARAMETER_VALUE_MAX_LEN] = {0}; + char *str = str_parms_to_str(query); + + ALOGV_IF(str != NULL, "%s: enter %s", __func__, str); + free(str); + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_AUDIO_MODE, value, + sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_KEY_AUDIO_MODE, adev->mode); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_ALL_CALL_STATES, + value, sizeof(value)); + if (ret >= 0) { + ret = get_all_call_states_str(adev, value); + if (ret) { + ALOGE("%s: Error fetching call states, err:%d", __func__, ret); + return; + } + str_parms_add_str(reply, AUDIO_PARAMETER_KEY_ALL_CALL_STATES, value); + } + + str = str_parms_to_str(reply); + ALOGV_IF(str != NULL, "%s: exit: returns \"%s\"", __func__, str); + free(str); +} + +#ifdef INCALL_MUSIC_ENABLED +int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev, + struct stream_out *out) +{ + uint32_t session_id = 0; + + session_id = get_session_id_with_state(adev, CALL_LOCAL_HOLD); + if (session_id == VOICE_VSID) { + out->usecase = USECASE_INCALL_MUSIC_UPLINK; + } else if (session_id == VOICE2_VSID) { + out->usecase = USECASE_INCALL_MUSIC_UPLINK2; + } else { + ALOGE("%s: Invalid session id %x", __func__, session_id); + return -EINVAL; + } + + out->config = pcm_config_incall_music; + out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_MONO; + out->channel_mask = AUDIO_CHANNEL_OUT_MONO; + + return 0; +} +#endif + diff --git a/hal/voice_extn/voice_extn.h b/hal/voice_extn/voice_extn.h new file mode 100644 index 00000000..fcc16ec1 --- /dev/null +++ b/hal/voice_extn/voice_extn.h @@ -0,0 +1,99 @@ +/* + * 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 VOICE_EXTN_H +#define VOICE_EXTN_H + +#ifdef MULTI_VOICE_SESSION_ENABLED +int voice_extn_start_call(struct audio_device *adev); +int voice_extn_stop_call(struct audio_device *adev); +int voice_extn_get_session_from_use_case(struct audio_device *adev, + const audio_usecase_t usecase_id, + struct voice_session **session); +void voice_extn_init(struct audio_device *adev); +int voice_extn_set_parameters(struct audio_device *adev, + struct str_parms *parms); +void voice_extn_get_parameters(const struct audio_device *adev, + struct str_parms *query, + struct str_parms *reply); +int voice_extn_is_in_call(struct audio_device *adev, bool *in_call); +int voice_extn_is_in_call_rec_stream(struct stream_in *in, bool *in_call_rec); +int voice_extn_get_active_session_id(struct audio_device *adev, + uint32_t *session_id); +#else +static int voice_extn_start_call(struct audio_device *adev __unused) +{ + return -ENOSYS; +} + +static int voice_extn_stop_call(struct audio_device *adev __unused) +{ + return -ENOSYS; +} + +static int voice_extn_get_session_from_use_case(struct audio_device *adev __unused, + const audio_usecase_t usecase_id __unused, + struct voice_session **session __unused) +{ + return -ENOSYS; +} + +static void voice_extn_init(struct audio_device *adev __unused) +{ +} + +static int voice_extn_set_parameters(struct audio_device *adev __unused, + struct str_parms *parms __unused) +{ + return -ENOSYS; +} + +static void voice_extn_get_parameters(const struct audio_device *adev __unused, + struct str_parms *query __unused, + struct str_parms *reply __unused) +{ +} + +static int voice_extn_is_in_call(struct audio_device *adev __unused, bool *in_call __unused) +{ + return -ENOSYS; +} + +static int voice_extn_is_in_call_rec_stream(struct stream_in *in __unused, bool *in_call_rec __unused) +{ + return -ENOSYS; +} + +static int voice_extn_get_active_session_id(struct audio_device *adev __unused, + uint32_t *session_id __unused) +{ + return -ENOSYS; +} + +#endif + +#ifdef INCALL_MUSIC_ENABLED +int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev, + struct stream_out *out); +#else +static int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev __unused, + struct stream_out *out __unused) +{ + return -ENOSYS; +} +#endif + +#endif //VOICE_EXTN_H |
