| /* |
| * Copyright (c) 2013-2017, 2020, The Linux Foundation. All rights reserved. |
| * Not a contribution. |
| * |
| * Copyright (C) 2013 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 "compress_voip" |
| /*#define LOG_NDEBUG 0*/ |
| #define LOG_NDDEBUG 0 |
| |
| #include <errno.h> |
| #include <pthread.h> |
| #include <stdint.h> |
| #include <sys/time.h> |
| #include <stdlib.h> |
| #include <math.h> |
| #include <log/log.h> |
| #include <cutils/str_parms.h> |
| #include <cutils/properties.h> |
| |
| #include "audio_hw.h" |
| #include "platform_api.h" |
| #include "platform.h" |
| #include "voice_extn.h" |
| |
| #ifdef DYNAMIC_LOG_ENABLED |
| #include <log_xml_parser.h> |
| #define LOG_MASK HAL_MOD_FILE_COMPR_VOIP |
| #include <log_utils.h> |
| #endif |
| |
| #define COMPRESS_VOIP_IO_BUF_SIZE_NB 320 |
| #define COMPRESS_VOIP_IO_BUF_SIZE_WB 640 |
| #define COMPRESS_VOIP_IO_BUF_SIZE_SWB 1280 |
| #define COMPRESS_VOIP_IO_BUF_SIZE_FB 1920 |
| |
| struct pcm_config pcm_config_voip_nb = { |
| .channels = 1, |
| .rate = 8000, /* changed when the stream is opened */ |
| .period_size = COMPRESS_VOIP_IO_BUF_SIZE_NB/2, |
| .period_count = 10, |
| .format = PCM_FORMAT_S16_LE, |
| .stop_threshold = INT_MAX, |
| }; |
| |
| struct pcm_config pcm_config_voip_wb = { |
| .channels = 1, |
| .rate = 16000, /* changed when the stream is opened */ |
| .period_size = COMPRESS_VOIP_IO_BUF_SIZE_WB/2, |
| .period_count = 10, |
| .format = PCM_FORMAT_S16_LE, |
| .stop_threshold = INT_MAX, |
| }; |
| |
| struct pcm_config pcm_config_voip_swb = { |
| .channels = 1, |
| .rate = 32000, /* changed when the stream is opened */ |
| .period_size = COMPRESS_VOIP_IO_BUF_SIZE_SWB/2, |
| .period_count = 10, |
| .format = PCM_FORMAT_S16_LE, |
| .stop_threshold = INT_MAX, |
| }; |
| |
| struct pcm_config pcm_config_voip_fb = { |
| .channels = 1, |
| .rate = 48000, /* changed when the stream is opened */ |
| .period_size = COMPRESS_VOIP_IO_BUF_SIZE_FB/2, |
| .period_count = 10, |
| .format = PCM_FORMAT_S16_LE, |
| .stop_threshold = INT_MAX, |
| }; |
| |
| struct voip_data { |
| struct pcm *pcm_rx; |
| struct pcm *pcm_tx; |
| struct stream_out *out_stream; |
| uint32_t out_stream_count; |
| uint32_t in_stream_count; |
| uint32_t sample_rate; |
| }; |
| |
| #define MODE_PCM 0xC |
| |
| #define AUDIO_PARAMETER_KEY_VOIP_RATE "voip_rate" |
| #define AUDIO_PARAMETER_KEY_VOIP_DTX_MODE "dtx_on" |
| #define AUDIO_PARAMETER_VALUE_VOIP_TRUE "true" |
| #define AUDIO_PARAMETER_KEY_VOIP_CHECK "voip_flag" |
| #define AUDIO_PARAMETER_KEY_VOIP_OUT_STREAM_COUNT "voip_out_stream_count" |
| #define AUDIO_PARAMETER_KEY_VOIP_SAMPLE_RATE "voip_sample_rate" |
| |
| static struct voip_data voip_data = { |
| .pcm_rx = NULL, |
| .pcm_tx = NULL, |
| .out_stream = NULL, |
| .out_stream_count = 0, |
| .in_stream_count = 0, |
| .sample_rate = 0 |
| }; |
| |
| static int voip_set_volume(struct audio_device *adev, int volume); |
| static int voip_set_mic_mute(struct audio_device *adev, bool state); |
| static int voip_set_mode(struct audio_device *adev, int format); |
| static int voip_set_rate(struct audio_device *adev, int rate); |
| static int voip_set_dtx(struct audio_device *adev, bool enable); |
| static int voip_stop_call(struct audio_device *adev); |
| static int voip_start_call(struct audio_device *adev, |
| struct pcm_config *voip_config); |
| |
| static int audio_format_to_voip_mode(int format) |
| { |
| int mode = AUDIO_FORMAT_INVALID; |
| |
| if (format == AUDIO_FORMAT_PCM_16_BIT) { |
| mode = MODE_PCM; |
| } |
| return mode; |
| } |
| |
| static int voip_set_volume(struct audio_device *adev, int volume) |
| { |
| struct mixer_ctl *ctl; |
| const char *mixer_ctl_name = "Voip Rx Gain"; |
| int vol_index = 0; |
| long set_values[ ] = {0, |
| DEFAULT_VOLUME_RAMP_DURATION_MS}; |
| |
| ALOGV("%s: enter", __func__); |
| |
| /* 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. |
| */ |
| 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) { |
| ALOGE("%s: Could not get ctl for mixer cmd - %s", |
| __func__, mixer_ctl_name); |
| return -EINVAL; |
| } |
| ALOGV("%s: Setting voip volume index: %ld", __func__, set_values[0]); |
| mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); |
| |
| ALOGV("%s: exit", __func__); |
| return 0; |
| } |
| |
| static int voip_set_mic_mute(struct audio_device *adev, bool state) |
| { |
| struct mixer_ctl *ctl; |
| const char *mixer_ctl_name = "Voip Tx Mute"; |
| long set_values[ ] = {0, |
| DEFAULT_VOLUME_RAMP_DURATION_MS}; |
| |
| ALOGV("%s: enter, state=%d", __func__, state); |
| |
| if (adev->mode == AUDIO_MODE_IN_COMMUNICATION) { |
| 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; |
| } |
| mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); |
| } |
| |
| ALOGV("%s: exit", __func__); |
| return 0; |
| } |
| |
| static int voip_set_mode(struct audio_device *adev, int format) |
| { |
| struct mixer_ctl *ctl; |
| const char *mixer_ctl_name = "Voip Mode Config"; |
| long set_values[ ] = {0}; |
| int mode; |
| |
| ALOGD("%s: enter, format=%d", __func__, format); |
| |
| mode = audio_format_to_voip_mode(format); |
| ALOGD("%s: Derived mode = %d", __func__, mode); |
| |
| set_values[0] = mode; |
| 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; |
| } |
| mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); |
| |
| ALOGV("%s: exit", __func__); |
| return 0; |
| } |
| |
| static int voip_set_rate(struct audio_device *adev, int rate) |
| { |
| struct mixer_ctl *ctl; |
| const char *mixer_ctl_name = "Voip Rate Config"; |
| long set_values[ ] = {0}; |
| |
| ALOGD("%s: enter, rate=%d", __func__, rate); |
| |
| set_values[0] = rate; |
| 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; |
| } |
| mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); |
| |
| ALOGV("%s: exit", __func__); |
| return 0; |
| } |
| |
| static int voip_set_dtx(struct audio_device *adev, bool enable) |
| { |
| struct mixer_ctl *ctl; |
| const char *mixer_ctl_name = "Voip Dtx Mode"; |
| long set_values[ ] = {0}; |
| |
| ALOGD("%s: enter, enable=%d", __func__, enable); |
| |
| set_values[0] = enable; |
| 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; |
| } |
| mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); |
| |
| ALOGV("%s: exit", __func__); |
| return 0; |
| } |
| |
| int compress_voip_set_parameters(struct audio_device *adev, |
| struct str_parms *parms) |
| { |
| char value[32]={0}; |
| int ret = 0, err, rate; |
| bool flag; |
| char *kv_pairs = str_parms_to_str(parms); |
| |
| ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs); |
| |
| err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_RATE, |
| value, sizeof(value)); |
| if (err >= 0) { |
| rate = atoi(value); |
| voip_set_rate(adev, rate); |
| } |
| |
| memset(value, 0, sizeof(value)); |
| err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_DTX_MODE, |
| value, sizeof(value)); |
| if (err >= 0) { |
| flag = false; |
| if (strcmp(value, AUDIO_PARAMETER_VALUE_VOIP_TRUE) == 0) |
| flag = true; |
| voip_set_dtx(adev, flag); |
| } |
| |
| ALOGV("%s: exit", __func__); |
| free(kv_pairs); |
| return ret; |
| } |
| |
| void compress_voip_get_parameters(struct str_parms *query, |
| struct str_parms *reply) |
| { |
| int ret; |
| char value[32]={0}; |
| |
| ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_OUT_STREAM_COUNT, |
| value, sizeof(value)); |
| if (ret >= 0) { |
| str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_OUT_STREAM_COUNT, |
| voip_data.out_stream_count); |
| } |
| |
| ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_SAMPLE_RATE, |
| value, sizeof(value)); |
| if (ret >= 0) { |
| str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_SAMPLE_RATE, |
| voip_data.sample_rate); |
| } |
| } |
| |
| void compress_voip_out_get_parameters(struct stream_out *out, |
| struct str_parms *query, |
| struct str_parms *reply) |
| { |
| int ret; |
| char value[32]={0}; |
| |
| ALOGD("%s: enter", __func__); |
| |
| ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_CHECK, value, sizeof(value)); |
| |
| if (ret >= 0) { |
| if (out->usecase == USECASE_COMPRESS_VOIP_CALL) |
| str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, true); |
| else |
| str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, false); |
| } |
| |
| ALOGV("%s: exit", __func__); |
| } |
| |
| void compress_voip_in_get_parameters(struct stream_in *in, |
| struct str_parms *query, |
| struct str_parms *reply) |
| { |
| int ret; |
| char value[32]={0}; |
| char *kv_pairs = NULL; |
| |
| ALOGV("%s: enter", __func__); |
| |
| ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_CHECK, value, sizeof(value)); |
| |
| if (ret >= 0) { |
| if (in->usecase == USECASE_COMPRESS_VOIP_CALL) |
| str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, true); |
| else |
| str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, false); |
| } |
| |
| kv_pairs = str_parms_to_str(reply); |
| ALOGD_IF(kv_pairs != NULL, "%s: exit: return - %s", __func__, kv_pairs); |
| free(kv_pairs); |
| } |
| |
| int compress_voip_out_get_buffer_size(struct stream_out *out) |
| { |
| if (out->config.rate == 48000) |
| return COMPRESS_VOIP_IO_BUF_SIZE_FB; |
| else if (out->config.rate== 32000) |
| return COMPRESS_VOIP_IO_BUF_SIZE_SWB; |
| else if (out->config.rate == 16000) |
| return COMPRESS_VOIP_IO_BUF_SIZE_WB; |
| else |
| return COMPRESS_VOIP_IO_BUF_SIZE_NB; |
| |
| } |
| |
| int compress_voip_in_get_buffer_size(struct stream_in *in) |
| { |
| if (in->config.rate == 48000) |
| return COMPRESS_VOIP_IO_BUF_SIZE_FB; |
| else if (in->config.rate== 32000) |
| return COMPRESS_VOIP_IO_BUF_SIZE_SWB; |
| else if (in->config.rate == 16000) |
| return COMPRESS_VOIP_IO_BUF_SIZE_WB; |
| else |
| return COMPRESS_VOIP_IO_BUF_SIZE_NB; |
| } |
| |
| int compress_voip_open_output_stream(struct stream_out *out) |
| { |
| int ret; |
| |
| ALOGD("%s: enter", __func__); |
| |
| out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_MONO; |
| out->channel_mask = AUDIO_CHANNEL_OUT_MONO; |
| out->usecase = USECASE_COMPRESS_VOIP_CALL; |
| if (out->sample_rate == 48000) |
| out->config = pcm_config_voip_fb; |
| else if (out->sample_rate == 32000) |
| out->config = pcm_config_voip_swb; |
| else if (out->sample_rate == 16000) |
| out->config = pcm_config_voip_wb; |
| else |
| out->config = pcm_config_voip_nb; |
| |
| voip_data.out_stream = out; |
| voip_data.out_stream_count++; |
| voip_data.sample_rate = out->sample_rate; |
| ret = voip_set_mode(out->dev, out->format); |
| |
| ALOGV("%s: exit", __func__); |
| return ret; |
| } |
| |
| int compress_voip_open_input_stream(struct stream_in *in) |
| { |
| int ret; |
| |
| ALOGD("%s: enter", __func__); |
| |
| if ((voip_data.sample_rate != 0) && |
| (voip_data.sample_rate != in->config.rate)) { |
| ret = -ENOTSUP; |
| goto done; |
| } else { |
| voip_data.sample_rate = in->config.rate; |
| } |
| |
| ret = voip_set_mode(in->dev, in->format); |
| if (ret < 0) |
| goto done; |
| |
| in->usecase = USECASE_COMPRESS_VOIP_CALL; |
| if (in->config.rate == 48000) |
| in->config = pcm_config_voip_fb; |
| else if (in->config.rate == 32000) |
| in->config = pcm_config_voip_swb; |
| else if (in->config.rate == 16000) |
| in->config = pcm_config_voip_wb; |
| else |
| in->config = pcm_config_voip_nb; |
| |
| voip_data.in_stream_count++; |
| |
| done: |
| ALOGV("%s: exit, ret=%d", __func__, ret); |
| return ret; |
| } |
| |
| int compress_voip_start_output_stream(struct stream_out *out) |
| { |
| int ret = 0; |
| struct audio_device *adev = out->dev; |
| struct audio_usecase *uc_info; |
| |
| ALOGD("%s: enter", __func__); |
| |
| if (CARD_STATUS_OFFLINE == out->card_status || |
| CARD_STATUS_OFFLINE == adev->card_status) { |
| ret = -ENETRESET; |
| ALOGE("%s: sound card is not active/SSR returning error %d ", __func__, ret); |
| goto error; |
| } |
| |
| if (is_sco_out_device_type(&out->device_list)) { |
| if (!adev->bt_sco_on) { |
| ALOGE("%s: SCO profile is not ready, return error", __func__); |
| ret = -EAGAIN; |
| goto error; |
| } |
| } |
| |
| if (!voip_data.out_stream_count) |
| ret = compress_voip_open_output_stream(out); |
| |
| ret = voip_start_call(adev, &out->config); |
| out->pcm = voip_data.pcm_rx; |
| uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); |
| if (uc_info) { |
| uc_info->stream.out = out; |
| assign_devices(&uc_info->device_list, &out->device_list); |
| } else { |
| ret = -EINVAL; |
| ALOGE("%s: exit(%d): failed to get use case info", __func__, ret); |
| goto error; |
| } |
| |
| error: |
| ALOGV("%s: exit: status(%d)", __func__, ret); |
| return ret; |
| } |
| |
| int compress_voip_start_input_stream(struct stream_in *in) |
| { |
| int ret = 0; |
| struct audio_device *adev = in->dev; |
| |
| ALOGD("%s: enter", __func__); |
| |
| if (CARD_STATUS_OFFLINE == in->card_status || |
| CARD_STATUS_OFFLINE == adev->card_status) { |
| ret = -ENETRESET; |
| ALOGE("%s: sound card is not active/SSR returning error %d ", __func__, ret); |
| goto error; |
| } |
| |
| if (is_sco_in_device_type(&in->device_list) && !adev->bt_sco_on) { |
| ret = -EIO; |
| ALOGE("%s SCO is not ready return error %d", __func__,ret); |
| goto error; |
| } |
| |
| if (!voip_data.in_stream_count) |
| ret = compress_voip_open_input_stream(in); |
| |
| ret = voip_start_call(adev, &in->config); |
| in->pcm = voip_data.pcm_tx; |
| |
| error: |
| ALOGV("%s: exit: status(%d)", __func__, ret); |
| return ret; |
| } |
| |
| int compress_voip_close_output_stream(struct audio_stream *stream) |
| { |
| struct stream_out *out = (struct stream_out *)stream; |
| struct audio_device *adev = out->dev; |
| int ret = 0; |
| |
| ALOGD("%s: enter", __func__); |
| if (voip_data.out_stream_count > 0) { |
| voip_data.out_stream_count--; |
| ret = voip_stop_call(adev); |
| voip_data.out_stream = NULL; |
| out->pcm = NULL; |
| } |
| |
| ALOGV("%s: exit: status(%d)", __func__, ret); |
| return ret; |
| } |
| |
| int compress_voip_close_input_stream(struct audio_stream *stream) |
| { |
| struct stream_in *in = (struct stream_in *)stream; |
| struct audio_device *adev = in->dev; |
| int status = 0; |
| |
| ALOGD("%s: enter", __func__); |
| |
| if(voip_data.in_stream_count > 0) { |
| voip_data.in_stream_count--; |
| status = voip_stop_call(adev); |
| in->pcm = NULL; |
| } |
| |
| ALOGV("%s: exit: status(%d)", __func__, status); |
| return status; |
| } |
| |
| |
| int compress_voip_set_volume(struct audio_device *adev, float volume) |
| { |
| int vol, err = 0; |
| |
| ALOGV("%s: enter", __func__); |
| |
| 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 = voip_set_volume(adev, vol); |
| |
| ALOGV("%s: exit: status(%d)", __func__, err); |
| |
| return err; |
| } |
| |
| int compress_voip_set_mic_mute(struct audio_device *adev, bool state) |
| { |
| int err = 0; |
| |
| ALOGV("%s: enter", __func__); |
| |
| err = voip_set_mic_mute(adev, state); |
| |
| ALOGV("%s: exit: status(%d)", __func__, err); |
| return err; |
| } |
| |
| bool compress_voip_pcm_prop_check() |
| { |
| char prop_value[PROPERTY_VALUE_MAX] = {0}; |
| |
| property_get("vendor.voice.path.for.pcm.voip", prop_value, "0"); |
| if (!strncmp("true", prop_value, sizeof("true"))) |
| { |
| ALOGD("%s: VoIP PCM property is enabled", __func__); |
| return true; |
| } |
| else |
| return false; |
| } |
| |
| bool compress_voip_is_active(const struct audio_device *adev) |
| { |
| struct audio_usecase *voip_usecase = NULL; |
| voip_usecase = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); |
| |
| if (voip_usecase != NULL) |
| return true; |
| else |
| return false; |
| } |
| |
| bool compress_voip_is_format_supported(audio_format_t format) |
| { |
| if (format == AUDIO_FORMAT_PCM_16_BIT && |
| compress_voip_pcm_prop_check()) |
| return true; |
| else |
| return false; |
| } |
| |
| bool compress_voip_is_config_supported(struct audio_config *config) |
| { |
| bool ret = false; |
| |
| ret = compress_voip_is_format_supported(config->format); |
| if (ret) { |
| if ((popcount(config->channel_mask) == 1) && |
| (config->sample_rate == 8000 || config->sample_rate == 16000 || |
| config->sample_rate == 32000 || config->sample_rate == 48000)) |
| ret = ((voip_data.sample_rate == 0) ? true: |
| (voip_data.sample_rate == config->sample_rate)); |
| else |
| ret = false; |
| } |
| return ret; |
| } |
| |
| bool compress_voip_is_started(struct audio_device *adev) |
| { |
| bool ret = false; |
| if (compress_voip_is_active(adev) && |
| voip_data.pcm_tx && voip_data.pcm_rx) |
| ret = true; |
| |
| return ret; |
| } |
| |
| static int voip_stop_call(struct audio_device *adev) |
| { |
| int ret = 0; |
| struct audio_usecase *uc_info; |
| struct listnode *node; |
| struct stream_out *out = NULL; |
| |
| ALOGD("%s: enter, out_stream_count=%d, in_stream_count=%d", |
| __func__, voip_data.out_stream_count, voip_data.in_stream_count); |
| |
| if (!voip_data.out_stream_count && !voip_data.in_stream_count) { |
| voip_data.sample_rate = 0; |
| uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); |
| if (uc_info == NULL) { |
| ALOGE("%s: Could not find the usecase (%d) in the list", |
| __func__, USECASE_COMPRESS_VOIP_CALL); |
| return -EINVAL; |
| } |
| voice_set_sidetone(adev, uc_info->out_snd_device, false); |
| |
| /* 1. Close the PCM devices */ |
| if (voip_data.pcm_rx) { |
| pcm_close(voip_data.pcm_rx); |
| voip_data.pcm_rx = NULL; |
| } |
| if (voip_data.pcm_tx) { |
| pcm_close(voip_data.pcm_tx); |
| voip_data.pcm_tx = NULL; |
| } |
| |
| /* 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); |
| |
| // restore device for other active usecases |
| list_for_each(node, &adev->usecase_list) { |
| uc_info = node_to_item(node, struct audio_usecase, list); |
| out = uc_info->stream.out; |
| if (out && adev->adm_register_output_stream |
| && adev->adm_on_routing_change) { |
| adev->adm_register_output_stream(adev->adm_data, |
| out->handle, |
| out->flags); |
| adev->adm_on_routing_change(adev->adm_data, |
| out->handle); |
| } |
| select_devices(adev, uc_info->id); |
| } |
| } else { |
| ALOGV("%s: unexpected because out_stream_count=%d, in_stream_count=%d", |
| __func__, voip_data.out_stream_count, voip_data.in_stream_count); |
| uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); |
| if (uc_info && !voip_data.out_stream_count) |
| uc_info->stream.out = adev->primary_output; |
| ret = -EINVAL; |
| } |
| |
| ALOGV("%s: exit: status(%d)", __func__, ret); |
| return ret; |
| } |
| |
| static int voip_start_call(struct audio_device *adev, |
| struct pcm_config *voip_config) |
| { |
| int ret = 0; |
| struct audio_usecase *uc_info; |
| int pcm_dev_rx_id, pcm_dev_tx_id; |
| unsigned int flags = PCM_OUT | PCM_MONOTONIC; |
| |
| ALOGD("%s: enter", __func__); |
| |
| uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); |
| if (uc_info == NULL) { |
| ALOGV("%s: voip usecase is added to the list", __func__); |
| uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); |
| |
| if (!uc_info) { |
| ALOGE("failed to allocate voip usecase mem"); |
| return -ENOMEM; |
| } |
| |
| uc_info->id = USECASE_COMPRESS_VOIP_CALL; |
| uc_info->type = VOIP_CALL; |
| if (voip_data.out_stream) |
| uc_info->stream.out = voip_data.out_stream; |
| else |
| uc_info->stream.out = adev->primary_output; |
| uc_info->in_snd_device = SND_DEVICE_NONE; |
| uc_info->out_snd_device = SND_DEVICE_NONE; |
| list_init(&uc_info->device_list); |
| |
| list_add_tail(&adev->usecase_list, &uc_info->list); |
| |
| select_devices(adev, USECASE_COMPRESS_VOIP_CALL); |
| |
| if (uc_info->in_snd_device == SND_DEVICE_NONE && |
| uc_info->out_snd_device == SND_DEVICE_NONE) { |
| ALOGD("No valid input output device return"); |
| ret = -EIO; |
| goto error_start_voip; |
| } |
| |
| 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_voip; |
| } |
| |
| ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)", |
| __func__, adev->snd_card, pcm_dev_tx_id); |
| voip_data.pcm_tx = pcm_open(adev->snd_card, |
| pcm_dev_tx_id, |
| PCM_IN|PCM_MONOTONIC, voip_config); |
| if (voip_data.pcm_tx && !pcm_is_ready(voip_data.pcm_tx)) { |
| ALOGE("%s: %s", __func__, pcm_get_error(voip_data.pcm_tx)); |
| pcm_close(voip_data.pcm_tx); |
| voip_data.pcm_tx = NULL; |
| ret = -EIO; |
| goto error_start_voip; |
| } |
| |
| ALOGD("%s: Opening PCM playback device card_id(%d) device_id(%d)", |
| __func__, adev->snd_card, pcm_dev_rx_id); |
| voip_data.pcm_rx = pcm_open(adev->snd_card, |
| pcm_dev_rx_id, |
| flags, voip_config); |
| if (voip_data.pcm_rx && !pcm_is_ready(voip_data.pcm_rx)) { |
| ALOGE("%s: %s", __func__, pcm_get_error(voip_data.pcm_rx)); |
| pcm_close(voip_data.pcm_rx); |
| voip_data.pcm_rx = NULL; |
| if (voip_data.pcm_tx) { |
| pcm_close(voip_data.pcm_tx); |
| voip_data.pcm_tx = NULL; |
| } |
| ret = -EIO; |
| goto error_start_voip; |
| } |
| |
| pcm_start(voip_data.pcm_tx); |
| pcm_start(voip_data.pcm_rx); |
| |
| voice_set_sidetone(adev, uc_info->out_snd_device, true); |
| compress_voip_set_volume(adev, adev->voice.volume); |
| } else { |
| ALOGV("%s: voip usecase is already enabled", __func__); |
| if (voip_data.out_stream) |
| uc_info->stream.out = voip_data.out_stream; |
| else |
| uc_info->stream.out = adev->primary_output; |
| select_devices(adev, USECASE_COMPRESS_VOIP_CALL); |
| } |
| |
| return 0; |
| |
| error_start_voip: |
| voip_stop_call(adev); |
| |
| ALOGV("%s: exit: status(%d)", __func__, ret); |
| return ret; |
| } |