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