diff --git a/primary_new/Android.mk b/primary_new/Android.mk new file mode 100755 index 0000000..4f7a020 --- /dev/null +++ b/primary_new/Android.mk @@ -0,0 +1,67 @@ +# Copyright (C) 2012 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. + +ifeq ($(INTEL_AUDIO_HAL),audio) + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + config.c \ + utils.c \ + audio_hw.c + +LOCAL_SHARED_LIBRARIES := \ + libexpat \ + liblog \ + libcutils \ + libaudioutils \ + libtinyalsa \ + libsafestring \ + libaudioroute \ + libdl + +LOCAL_C_INCLUDES += \ + external/tinyalsa/include \ + $(LOCAL_PATH)/include \ + $(call include-path-for, audio-utils) \ + $(call include-path-for, audio-route) \ + $(call include-path-for, audio-effects) + +LOCAL_CFLAGS :=\ + -fwrapv \ + -D_FORTIFY_SOURCE=2 \ + -fstack-protector-strong \ + -Wno-conversion-null \ + -Wnull-dereference \ + -Werror \ + -Warray-bounds \ + -Wformat -Wformat-security \ + -Werror=format-security + +#Preferred paths for all vendor hals /vendor/lib/hw +LOCAL_PROPRIETARY_MODULE := true + +LOCAL_HEADER_LIBRARIES += libhardware_headers + +LOCAL_MODULE := audio.primary.new.$(TARGET_BOARD_PLATFORM) + +LOCAL_MODULE_RELATIVE_PATH := hw + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +endif # INTEL_AUDIO_HAL diff --git a/primary_new/audio_dbg.h b/primary_new/audio_dbg.h new file mode 100644 index 0000000..caf5ca6 --- /dev/null +++ b/primary_new/audio_dbg.h @@ -0,0 +1,18 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/*#define LOG_NDEBUG 0*/ + +//#define DEBUG_PCM_DUMP + +#ifdef DEBUG_PCM_DUMP +// To enable dumps, explicitly create "/vendor/dump/" folder and reboot device +FILE *sco_call_write = NULL; +FILE *sco_call_write_remapped = NULL; +FILE *sco_call_write_bt = NULL; +FILE *sco_call_read = NULL; +FILE *sco_call_read_remapped = NULL; +FILE *sco_call_read_bt = NULL; +FILE *out_write_dump = NULL; +FILE *in_read_dump = NULL; +#endif diff --git a/primary_new/audio_hw.c b/primary_new/audio_hw.c new file mode 100755 index 0000000..06aa807 --- /dev/null +++ b/primary_new/audio_hw.c @@ -0,0 +1,1482 @@ +/* + * Copyright (C) 2012 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_primary" + +#include +#include +#include +#include + +static void select_devices(struct audio_device *adev) +{ + int headphone_on; + int speaker_on; + int main_mic_on; + int headset_mic_on; + + headphone_on = adev->out_device & (AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE); + speaker_on = adev->out_device & AUDIO_DEVICE_OUT_SPEAKER; + main_mic_on = adev->in_device & AUDIO_DEVICE_IN_BUILTIN_MIC; + headset_mic_on = adev->in_device & AUDIO_DEVICE_IN_WIRED_HEADSET; + + audio_route_reset(adev->ar); + + if (speaker_on) + audio_route_apply_path(adev->ar, "speaker"); + if (headphone_on) + audio_route_apply_path(adev->ar, "headphone"); + if (main_mic_on) + audio_route_apply_path(adev->ar, "main-mic"); + if (headset_mic_on) + audio_route_apply_path(adev->ar, "headset-mic"); + + audio_route_update_mixer(adev->ar); + + ALOGV("%s : hp=%c speaker=%c main-mic=%c headset-mic=%c",__func__, + headphone_on ? 'y' : 'n', speaker_on ? 'y' : 'n', + main_mic_on ? 'y' : 'n', headset_mic_on ? 'y' : 'n' ); +} + +/* must be called with hw device and output stream mutexes locked */ +static void do_out_standby(struct stream_out *out) +{ + struct audio_device *adev = out->dev; + if (!out->standby) { + pcm_close(out->pcm); + out->pcm = NULL; + adev->active_out = NULL; + out->standby = true; + } +} + +/* must be called with hw device and input stream mutexes locked */ +static void do_in_standby(struct stream_in *in) +{ + struct audio_device *adev = in->dev; + if (!in->standby) { + pcm_close(in->pcm); + in->pcm = NULL; + adev->active_in = NULL; + in->standby = true; + } +} + +static int get_pcm_card(const char* name) +{ + char id_filepath[PATH_MAX] = {0}; + char number_filepath[PATH_MAX] = {0}; + ssize_t written; + + snprintf(id_filepath, sizeof(id_filepath), "/proc/asound/%s", name); + + written = readlink(id_filepath, number_filepath, sizeof(number_filepath)); + if (written < 0) { + ALOGE("Sound card %s does not exist\n", name); + return -1; + } else if (written >= (ssize_t)sizeof(id_filepath)) { + ALOGE("Sound card %s name is too long - setting default \n", name); + return -1; + } + ALOGI("Sound card %s exists\n", name); + return atoi(number_filepath + 4); +} + +void update_bt_card(struct audio_device *adev){ + adev->bt_card = get_pcm_card(AUDIO_BT_DRIVER_NAME); //update driver name if changed from BT side. +} + +/* must be called with hw device and output stream mutexes locked */ +static int start_output_stream(struct stream_out *out) +{ + struct audio_device *adev = out->dev; + + ALOGV("%s : config : [rate %d format %d channels %d]",__func__, + out->pcm_config->rate, out->pcm_config->format, out->pcm_config->channels); + + if (out->unavailable) { + ALOGV("start_output_stream: output not available"); + return -ENODEV; + } + +//[BT SCO VoIP Call + if(adev->in_sco_voip_call) { + ALOGD("%s : sco voip call active", __func__); + + ALOGV("%s : opening pcm [%d : %d] for config : [rate %d format %d channels %d]", __func__, adev->bt_card, PCM_DEVICE, + bt_out_config.rate, bt_out_config.format, bt_out_config.channels); + + out->pcm = pcm_open(adev->bt_card, PCM_DEVICE /*0*/, PCM_OUT, &bt_out_config); +//BT SCO VoIP Call] + } else { + ALOGI("PCM playback card selected = %d, \n", adev->card); + out->pcm = pcm_open(adev->card, PCM_DEVICE, PCM_OUT | PCM_NORESTART | PCM_MONOTONIC, out->pcm_config); + } + + if (!out->pcm) { + ALOGE("pcm_open(out) failed: device not found"); + return -ENODEV; + } else if (!pcm_is_ready(out->pcm)) { + ALOGE("pcm_open(out) failed: %s", pcm_get_error(out->pcm)); + pcm_close(out->pcm); + out->unavailable = true; + return -ENOMEM; + } + + adev->active_out = out; + + /* force mixer updates */ + select_devices(adev); + + return 0; +} + +/* must be called with hw device and input stream mutexes locked */ +static int start_input_stream(struct stream_in *in) +{ + struct audio_device *adev = in->dev; + +//[BT SCO VoIP Call + if(adev->in_sco_voip_call) { + ALOGD("%s : sco voip call active", __func__); + + ALOGV("%s : opening pcm [%d : %d] for config : [rate %d format %d channels %d]",__func__, adev->bt_card, PCM_DEVICE, + bt_in_config.rate, bt_in_config.format, bt_in_config.channels); + + in->pcm = pcm_open(adev->bt_card, PCM_DEVICE, PCM_IN, &bt_in_config); +//BT SCO VoIP Call] + } else { + ALOGI("PCM record card selected = %d, \n", adev->card); + + ALOGV("%s : config : [rate %d format %d channels %d]",__func__, + in->pcm_config->rate, in->pcm_config->format, in->pcm_config->channels); + + in->pcm = pcm_open(adev->cardc, PCM_DEVICE, PCM_IN, in->pcm_config); + } + + if (!in->pcm) { + return -ENODEV; + } else if (!pcm_is_ready(in->pcm)) { + ALOGE("pcm_open(in) failed: %s", pcm_get_error(in->pcm)); + pcm_close(in->pcm); + return -ENOMEM; + } + + adev->active_in = in; + + /* force mixer updates */ + select_devices(adev); + + return 0; +} + +/* API functions */ + +static int out_standby(struct audio_stream *stream) +{ + struct stream_out *out = (struct stream_out *)stream; + + ALOGV("out_standby"); + pthread_mutex_lock(&out->dev->lock); + pthread_mutex_lock(&out->lock); + do_out_standby(out); + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&out->dev->lock); + + return 0; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + ALOGV("%s : kvpairs : %s",__func__, kvpairs); + struct stream_out *out = (struct stream_out *)stream; + struct audio_device *adev = out->dev; + struct str_parms *parms; + char value[32]; + int ret; + int status = 0; + unsigned int val; + + parms = str_parms_create_str(kvpairs); + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + + pthread_mutex_lock(&adev->lock); + if (ret >= 0) { + val = atoi(value); + if ((adev->out_device != val) && (val != 0)) { + adev->out_device = val; + select_devices(adev); + } + +#if 0 // FIXME, does this need to replace above code ? + pthread_mutex_lock(&out->lock); + if (((adev->devices & AUDIO_DEVICE_OUT_ALL) != val) && (val != 0)) { + adev->devices &= ~AUDIO_DEVICE_OUT_ALL; + adev->devices |= val; + } + pthread_mutex_unlock(&out->lock); +#endif + + } + pthread_mutex_unlock(&adev->lock); + + str_parms_destroy(parms); + return status; +} + +static char *out_get_parameters(const struct audio_stream *stream, const char *keys) +{ + ALOGV("%s : keys : %s",__func__,keys); + struct stream_out *out = (struct stream_out *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str_parm = NULL; + char value[256]; + struct str_parms *reply = str_parms_create(); + int ret; + + if(reply == NULL || query == NULL) { + if(reply != NULL) str_parms_destroy(reply); + if(query != NULL) str_parms_destroy(query); + return NULL; + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, "AUDIO_FORMAT_PCM_16_BIT"); + str_parm = str_parms_to_str(reply); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, out->req_config.sample_rate); + + if(str_parm != NULL) + str_parms_destroy((struct str_parms *)str_parm); + + str_parm = str_parms_to_str(reply); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, + (out->req_config.channel_mask == AUDIO_CHANNEL_OUT_MONO ? "AUDIO_CHANNEL_OUT_MONO" : "AUDIO_CHANNEL_OUT_STEREO")); + + if(str_parm != NULL) + str_parms_destroy((struct str_parms *)str_parm); + + str_parm = str_parms_to_str(reply); + } + + str_parms_destroy(query); + str_parms_destroy(reply); + + ALOGV("%s : returning keyValuePair %s",__func__, str_parm); + return str_parm; +} + +static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, + size_t bytes) +{ + int ret = 0; + struct stream_out *out = (struct stream_out *)stream; + struct audio_device *adev = out->dev; + size_t frame_size = audio_stream_out_frame_size(stream); + int16_t *out_buffer = (int16_t *)buffer; + unsigned int out_frames = bytes / frame_size; + + ALOGV("out_write: bytes: %zu", bytes); + + /* + * acquiring hw device mutex systematically is useful if a low + * priority thread is waiting on the output stream mutex - e.g. + * executing out_set_parameters() while holding the hw device + * mutex + */ + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&out->lock); + + if(adev->out_needs_standby) { + do_out_standby(out); + adev->out_needs_standby = false; + } + + if (out->standby) { + if(!adev->is_hfp_call_active) { + ret = start_output_stream(out); + } else { + ret = -1; + } + if (ret != 0) { + pthread_mutex_unlock(&adev->lock); + goto exit; + } + out->standby = false; + } + pthread_mutex_unlock(&adev->lock); + +//[BT SCO VoIP Call + if(adev->in_sco_voip_call) { + ret = out_write_bt(out, adev, buffer, bytes); + if (ret != 0) + goto exit; +//BT SCO VoIP Call] + } else { + /* Normal pcm out to primary card */ + ret = pcm_write(out->pcm, out_buffer, out_frames * frame_size); + +#ifdef DEBUG_PCM_DUMP + if(out_write_dump != NULL) { + fwrite(out_buffer, 1, out_frames * frame_size, out_write_dump); + } else { + ALOGD("%s : out_write_dump was NULL, no dump", __func__); + } +#endif + + if (ret == -EPIPE) { + /* In case of underrun, don't sleep since we want to catch up asap */ + pthread_mutex_unlock(&out->lock); + return ret; + } + } + + if (ret == 0) { + out->written += out_frames; + } + +exit: + pthread_mutex_unlock(&out->lock); + + if (ret != 0) { + ALOGW("out_write error: %d, sleeping...", ret); + usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) / + out_get_sample_rate(&stream->common)); + } + + return bytes; +} + +static int in_standby(struct audio_stream *stream) +{ + struct stream_in *in = (struct stream_in *)stream; + + pthread_mutex_lock(&in->dev->lock); + pthread_mutex_lock(&in->lock); + do_in_standby(in); + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&in->dev->lock); + + return 0; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct stream_in *in = (struct stream_in *)stream; + struct audio_device *adev = in->dev; + struct str_parms *parms; + char value[32]; + int ret; + int status = 0; + unsigned int val; + + parms = str_parms_create_str(kvpairs); + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + pthread_mutex_lock(&adev->lock); + if (ret >= 0) { + val = atoi(value) & ~AUDIO_DEVICE_BIT_IN; + if ((adev->in_device != val) && (val != 0)) { + adev->in_device = val; + select_devices(adev); + } + } + pthread_mutex_unlock(&adev->lock); + + str_parms_destroy(parms); + return status; +} + +static char * in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + ALOGV("%s : keys : %s",__func__,keys); + struct stream_in *in = (struct stream_in *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str_parm = NULL; + char value[256]; + struct str_parms *reply = str_parms_create(); + int ret; + + if(reply == NULL || query == NULL) { + if(reply != NULL) str_parms_destroy(reply); + if(query != NULL) str_parms_destroy(query); + return NULL; + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, "AUDIO_FORMAT_PCM_16_BIT"); + str_parm = str_parms_to_str(reply); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, in->req_config.sample_rate); + + if(str_parm != NULL) + str_parms_destroy((struct str_parms *)str_parm); + + str_parm = str_parms_to_str(reply); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, + (in->req_config.channel_mask == AUDIO_CHANNEL_IN_MONO ? "AUDIO_CHANNEL_IN_MONO" : "AUDIO_CHANNEL_IN_STEREO")); + + if(str_parm != NULL) + str_parms_destroy((struct str_parms *)str_parm); + + str_parm = str_parms_to_str(reply); + } + + str_parms_destroy(query); + str_parms_destroy(reply); + + ALOGV("%s : returning keyValuePair %s",__func__, str_parm); + return str_parm; +} + +static ssize_t in_read(struct audio_stream_in *stream, void* buffer, + size_t bytes) +{ + int ret = 0; + struct stream_in *in = (struct stream_in *)stream; + struct audio_device *adev = in->dev; + + ALOGV("%s : bytes_requested : %zu", __func__, bytes); + + /* + * acquiring hw device mutex systematically is useful if a low + * priority thread is waiting on the input stream mutex - e.g. + * executing in_set_parameters() while holding the hw device + * mutex + */ + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&in->lock); + + if(adev->in_needs_standby) { + do_in_standby(in); + adev->in_needs_standby = false; + } + + if (in->standby) { + if(!adev->is_hfp_call_active) { + ret = start_input_stream(in); + } else { + ret = -1; + } + if (ret == 0) + in->standby = 0; + } + pthread_mutex_unlock(&adev->lock); + + if (ret < 0) + goto exit; + +//[BT SCO VoIP Call + if(adev->in_sco_voip_call) { + ret = in_read_bt(in, adev, buffer, bytes); + if (ret != 0) + goto exit; +//BT SCO VoIP Call] + } else { + /* pcm read for primary card */ + ret = pcm_read(in->pcm, buffer, bytes); + +#ifdef DEBUG_PCM_DUMP + if(in_read_dump != NULL) { + fwrite(buffer, 1, bytes, in_read_dump); + } else { + ALOGD("%s : in_read_dump was NULL, no dump", __func__); + } +#endif + } + if (ret > 0) + ret = 0; + + /* + * Instead of writing zeroes here, we could trust the hardware + * to always provide zeroes when muted. + */ + if (ret == 0 && adev->mic_mute) + memset(buffer, 0, bytes); + +exit: + pthread_mutex_unlock(&in->lock); + if (ret < 0) + usleep(bytes * 1000000 / audio_stream_in_frame_size(stream) / + in_get_sample_rate(&stream->common)); + + return bytes; +} + +/* copied from libcutils/str_parms.c */ +static bool str_eq(void *key_a, void *key_b) { + return !strcmp((const char *)key_a, (const char *)key_b); +} + +/** + * use djb hash unless we find it inadequate. + * copied from libcutils/str_parms.c + */ +#ifdef __clang__ +__attribute__((no_sanitize("integer"))) +#endif +static int str_hash_fn(void *str) { + uint32_t hash = 5381; + char *p; + for (p = str; p && *p; p++) { + hash = ((hash << 5) + hash) + *p; + } + return (int)hash; +} + +static int adev_open_output_stream(struct audio_hw_device *dev, + audio_io_handle_t handle __unused, + audio_devices_t devices __unused, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out, + const char *address __unused) +{ + ALOGD("%s : requested config : [rate %d format %d channels %d flags %#x]",__func__, + config->sample_rate, config->format, popcount(config->channel_mask), flags); + + struct audio_device *adev = (struct audio_device *)dev; + struct stream_out *out; + struct pcm_params *params; + struct pcm_config *pcm_config = NULL; + bool isDummy = false; + bool isVirtioCard = false; + int card = -1, device = -1; + int ret = -1; + + struct stream_config *sc = audio_hal_config_get(adev->hal_config, address, 1 /*playback*/); + if (!sc || !sc->card_name || sc->device_id < 0) { + isDummy = true; + ALOGW("%s:%s : Incorrect parameters in the hal configuration, use dummy sound card instead", __func__, address); + } else { + card = get_pcm_card(sc->card_name); + device = sc->device_id; + pcm_config = &sc->pcm_config; + params = pcm_params_get(card, device, PCM_OUT); + if (card < 0 || !params) { + isDummy = true; + ALOGW("%s:%s : hw param get fail, request card=%d, device=%d", __func__, address, card, device); + } + free(params); + isVirtioCard = check_virito_card(card); + } + + if (isDummy) { + card = get_pcm_card("Dummy"); + device = PCM_DUMMY_DEVICE; + if(!pcm_config) { + pcm_config = &dummy_pcm_config_out; + } + } + + ALOGI("PCM playback card selected = %d, \n", adev->card); + out = (struct stream_out *)calloc(1, sizeof(struct stream_out)); + if (!out) { + free(params); + return -ENOMEM; + } + + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_buffer_size = out_get_buffer_size; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.standby = out_standby; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect; + out->stream.common.remove_audio_effect = out_remove_audio_effect; + out->stream.get_latency = out_get_latency; + out->stream.set_volume = out_set_volume; + out->stream.write = out_write; + out->stream.get_render_position = out_get_render_position; + out->stream.get_next_write_timestamp = out_get_next_write_timestamp; + out->stream.get_presentation_position = out_get_presentation_position; + + out->pcm_config = &pcm_config_out; + + out->written = 0; + +// VTS : Device doesn't support mono channel or sample_rate other than 48000 +// make a copy of requested config to feed it back if requested. + memcpy(&out->req_config, config, sizeof(struct audio_config)); + + out->dev = adev; + out->standby = true; + out->unavailable = false; + out->written = 0; + out->stream_config = pcm_config; + out->stream_card = card; + out->stream_device = device; + out->stream_virtio_card = isVirtioCard; + out->bus_address = NULL; + out->ar = NULL; + out->enabled_channels = BOTH_CHANNELS; + + if (sc && sc->mixer_path && sc->mixer_path->card_name && sc->mixer_path->mixer_path) { + card = get_pcm_card(sc->mixer_path->card_name); + if(card < 0) { + ALOGE("%s: Failed to get audio route card %s", __func__, + sc->mixer_path->card_name); + + adev_close_output_stream(dev, (struct audio_stream_out *)out); + return -ENOSYS; + } + + out->ar = audio_route_init(card, sc->mixer_path->mixer_path); + if (!out->ar) { + ALOGE("%s: Failed to init audio route controls, address %s, card %s", + __func__, address, sc->mixer_path->card_name); + + adev_close_output_stream(dev, (struct audio_stream_out *)out); + return -ENOSYS; + } + } + + config->format = out_get_format(&out->stream.common); + config->channel_mask = out_get_channels(&out->stream.common); + config->sample_rate = out_get_sample_rate(&out->stream.common); + + *stream_out = &out->stream; + + free(params); + + return 0; +} + +static void adev_close_output_stream(struct audio_hw_device *dev __unused, + struct audio_stream_out *stream) +{ + out_standby(&stream->common); + free(stream); +} + +//called with adev lock +static void stop_existing_output_input(struct audio_device *adev){ + ALOGD("%s during call scenario", __func__); + adev->in_needs_standby = true; + adev->out_needs_standby = true; +} + +static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) +{ + ALOGD("%s : kvpairs: %s", __func__, kvpairs); + + struct audio_device * adev = (struct audio_device *)dev; + char value[32]; + int ret; + struct str_parms *parms; + + parms = str_parms_create_str(kvpairs); + + if(parms == NULL) { + return 0; + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value, sizeof(value)); + if (ret >= 0) { + pthread_mutex_lock(&adev->lock); + if (strcmp(value, "true") == 0){ + stop_existing_output_input(adev); + adev->is_hfp_call_active = true; + } else { + adev->is_hfp_call_active = false; + } + pthread_mutex_unlock(&adev->lock); + } + +//[BT SCO VoIP Call + ret = str_parms_get_str(parms, AUDIO_PARAMETER_BT_SCO, value, sizeof(value)); + if (ret >= 0) { + pthread_mutex_lock(&adev->lock); + if (strcmp(value, "on") == 0){ + adev->in_sco_voip_call = true; + stop_existing_output_input(adev); + } else { + adev->in_sco_voip_call = false; + stop_existing_output_input(adev); + + release_resampler(adev->voip_in_resampler); + adev->voip_in_resampler = NULL; + release_resampler(adev->voip_out_resampler); + adev->voip_out_resampler = NULL; + } + pthread_mutex_unlock(&adev->lock); + } +//BT SCO VoIP Call] + + str_parms_destroy(parms); + return 0; +} + +static char * adev_get_parameters(const struct audio_hw_device *dev __unused, + const char *keys) +{ + ALOGV("%s : keys : %s",__func__,keys); + struct str_parms *query = str_parms_create_str(keys); + char value[256]; + int ret; + + if(query == NULL) { + return NULL; + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, value, sizeof(value)); + if (ret >= 0) { + str_parms_destroy(query); + return NULL; + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value)); + if(ret >= 0) { + ALOGE("%s : no support of TTY",__func__); + str_parms_destroy(query); + return NULL; + } + + str_parms_destroy(query); + return strdup(keys); +} + +static int adev_init_check(const struct audio_hw_device *dev __unused) +{ + ALOGV("adev_init_check"); + return 0; +} + +//Supported vol range [0:1], return OK for inrange volume request +static int adev_set_voice_volume(struct audio_hw_device *dev __unused, float volume) +{ + ALOGV("adev_set_voice_volume: %f : This platform provides no such handling", volume); + + int32_t ret = 0; + + if(volume < 0.0f){ + ret = -EINVAL; + } + + return ret; +} + +static int adev_set_master_volume(struct audio_hw_device *dev __unused, float volume __unused) +{ + ALOGV("adev_set_master_volume: %f", volume); + return -ENOSYS; +} + +static int adev_get_master_volume(struct audio_hw_device *dev __unused, float *volume __unused) +{ + ALOGV("adev_get_master_volume:"); + return -ENOSYS; +} + +static int adev_set_master_mute(struct audio_hw_device *dev __unused, bool muted __unused) +{ + ALOGV("adev_set_master_mute: %d", muted); + return -ENOSYS; +} + +static int adev_get_master_mute(struct audio_hw_device *dev __unused, bool *muted __unused) +{ + ALOGV("adev_get_master_mute: %d", *muted); + return -ENOSYS; +} +static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) +{ + ALOGD("%s : mode : %d", __func__, mode); + struct audio_device *adev = (struct audio_device *)dev; + + pthread_mutex_lock(&adev->lock); + stop_existing_output_input(adev); + pthread_mutex_unlock(&adev->lock); + + return 0; +} + +static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + ALOGV("adev_set_mic_mute: %d",state); + struct audio_device *adev = (struct audio_device *)dev; + adev->mic_mute = state; + return 0; +} + +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + ALOGV("adev_get_mic_mute"); + struct audio_device *adev = (struct audio_device *)dev; + *state = adev->mic_mute; + return 0; +} + +static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev __unused, + const struct audio_config *config) +{ + size_t size; + + /* + * take resampling into account and return the closest majoring + * multiple of 16 frames, as audioflinger expects audio buffers to + * be a multiple of 16 frames + */ + size = (pcm_config_in.period_size * config->sample_rate) / pcm_config_in.rate; + size = ((size + 15) / 16) * 16; + + return (size * popcount(config->channel_mask) * + audio_bytes_per_sample(config->format)); +} + +static int adev_open_input_stream(struct audio_hw_device *dev, + audio_io_handle_t handle __unused, + audio_devices_t devices __unused, + struct audio_config *config, + struct audio_stream_in **stream_in, + audio_input_flags_t flags __unused, + const char *address __unused, + audio_source_t source __unused) + +{ + ALOGD("%s : requested config : [rate %d format %d channels %d flags %#x]",__func__, + config->sample_rate, config->format, popcount(config->channel_mask), flags); + + struct audio_device *adev = (struct audio_device *)dev; + struct stream_in *in = NULL; + struct pcm_params *params = NULL; + struct pcm_config *pcm_config = NULL; + bool isDummy = false; + int card = -1, device = -1; + int ret = -1; + *stream_in = NULL; + + struct stream_config *sc = audio_hal_config_get(adev->hal_config, address, 0 /*capture*/); + if (!sc || !sc->card_name || sc->device_id < 0) { + isDummy = true; + ALOGW("%s:%s : Incorrect parameters in the hal configuration, use dummy sound card instead", __func__, address); + } else { + card = get_pcm_card(sc->card_name); + device = sc->device_id; + pcm_config = &sc->pcm_config; + params = pcm_params_get(card, device, PCM_IN); + if (card < 0 || !params) { + isDummy = true; + ALOGW("%s:%s : hw param get fail, request card=%d, device=%d", __func__, address, card, device); + } + + free(params); + } + + if (isDummy) { + card = get_pcm_card("Dummy"); + device = PCM_DUMMY_DEVICE; + + pcm_config = &dummy_pcm_config_in; + } + ALOGI("PCM capture card selected = %d, \n", adev->cardc); + + in = (struct stream_in *)calloc(1, sizeof(struct stream_in)); + if (!in) { + free(params); + return -ENOMEM; + } + + 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; + in->stream.common.get_channels = in_get_channels; + in->stream.common.get_format = in_get_format; + in->stream.common.set_format = in_set_format; + in->stream.common.standby = in_standby; + in->stream.common.dump = in_dump; + in->stream.common.set_parameters = in_set_parameters; + in->stream.common.get_parameters = in_get_parameters; + in->stream.common.add_audio_effect = in_add_audio_effect; + in->stream.common.remove_audio_effect = in_remove_audio_effect; + in->stream.set_gain = in_set_gain; + in->stream.read = in_read; + in->stream.get_input_frames_lost = in_get_input_frames_lost; + + in->dev = adev; + in->standby = true; + + in->pcm_config = &pcm_config_in; /* default PCM config */ + +// VTS : Device doesn't support mono channel or sample_rate other than 48000 +// make a copy of requested config to feed it back if requested. + memcpy(&in->req_config, config, sizeof(struct audio_config)); + + in->bus_address = NULL; + in->ar = NULL; + in->stream_config = pcm_config; + in->stream_card = card; + in->stream_device = device; + + if (sc && sc->mixer_path && sc->mixer_path->card_name && sc->mixer_path->mixer_path) { + card = get_pcm_card(sc->mixer_path->card_name); + if(card < 0) { + ALOGE("%s: Failed to get audio route card %s.", __func__, + sc->mixer_path->card_name); + + adev_close_input_stream(dev, (struct audio_stream_in *)in); + return -ENOSYS; + } + + in->ar = audio_route_init(card, sc->mixer_path->mixer_path); + if (!in->ar) { + ALOGE("%s: Failed to init audio route controls, address %s, card %s.", + __func__, address, sc->mixer_path->card_name); + + adev_close_input_stream(dev, (struct audio_stream_in *)in); + return -ENOSYS; + } + } + + *stream_in = &in->stream; + + free(params); + return 0; +} + +static void adev_close_input_stream(struct audio_hw_device *dev __unused, + struct audio_stream_in *stream) +{ + ALOGV("adev_close_input_stream..."); + + in_standby(&stream->common); + free(stream); +} + +static int adev_dump(const audio_hw_device_t *device __unused, int fd __unused) +{ + ALOGV("adev_dump"); + return 0; +} + +static int adev_get_microphones(const audio_hw_device_t *device __unused, struct audio_microphone_characteristic_t *mic_array, size_t *actual_mics) +{ + ALOGV("%s",__func__); + int32_t ret = 0; + *actual_mics = 1; + memset(&mic_array[0], 0, sizeof(mic_array[0])); + + return ret; +} + +static int adev_close(hw_device_t *device) +{ + ALOGV("adev_close"); + + struct audio_device *adev = (struct audio_device *)device; + + audio_route_free(adev->ar); + +#ifdef DEBUG_PCM_DUMP + if(sco_call_write != NULL) { + fclose(sco_call_write); + } + if(sco_call_write_remapped != NULL) { + fclose(sco_call_write_remapped); + } + if(sco_call_write_bt != NULL) { + fclose(sco_call_write_bt); + } + if(sco_call_read != NULL) { + fclose(sco_call_read); + } + if(sco_call_read_remapped != NULL) { + fclose(sco_call_read_remapped); + } + if(sco_call_read_bt != NULL) { + fclose(sco_call_read_bt); + } + if(out_write_dump != NULL) { + fclose(out_write_dump); + } + if(in_read_dump != NULL) { + fclose(in_read_dump); + } +#endif + + free(device); + return 0; +} + +static int mixer_setting_for_volume() +{ + struct mixer *mixer; + int card = 0; //assuming the default card 0, need to get this once we have other mixer controls + enum mixer_ctl_type mixer_type; + unsigned int num_values; + unsigned int i,id,j; + bool device_status; + int volume[2]; + struct mixer_ctl *vol_ctl1, *vol_ctl2; + + mixer = mixer_open(card); + if (!mixer) { + ALOGE(" Failed to open mixer\n"); + return -1; + } + //TODO: only two mixer controls passed from host, once we have other controls, pick it from xml + + vol_ctl1 = mixer_get_ctl_by_name(mixer, "DAC2 Playback Volume"); + vol_ctl2 = mixer_get_ctl_by_name(mixer, "DAC3 Playback Volume"); + + if (!vol_ctl1 || !vol_ctl2) { + ALOGE(": Error opening mixerVolumecontrol "); + mixer_close(mixer); + return -1; + } + volume[0] = DEVICE_MAX_VOL; + volume[1] = DEVICE_MAX_VOL; + ALOGV("Volume left and right %d %d \n",volume[0],volume[1]); + //set vol for device 1 + int retval1 = mixer_ctl_set_array(vol_ctl1, volume, 2); + if (retval1 < 0) { + ALOGE( ": Err setting volume dB value %x", volume[0]); + mixer_close(mixer); + return -1; + } + //set vol for device 2 + int retval2 = mixer_ctl_set_array(vol_ctl2, volume, 2); + if (retval2 < 0) { + ALOGE( ": Err setting volume dB value %x", volume[0]); + mixer_close(mixer); + return -1; + } + ALOGV( ": Successful in set volume"); + mixer_close(mixer); + return 0; +} + +/*audio port config info used for volume */ +static int adev_set_audio_port_config(struct audio_hw_device *dev , + const struct audio_port_config *config ) +{ + int ret = 0; + struct audio_device *adev = (struct audio_device *)dev; + char *bus_address = calloc(strlen(config->ext.device.address) + 1, sizeof(char)); + if (!bus_address){ + ALOGE("%s: free the allocated %s", __func__); + free(bus_address); + return -ENOMEM; + } + strncpy(bus_address, config->ext.device.address, strlen(config->ext.device.address)); + struct stream_out *out = hashmapGet(adev->out_bus_stream_map, bus_address); + if (out) { + pthread_mutex_lock(&out->lock); + int gainIndex = (config->gain.values[0] - out->gain_stage.min_value) / + out->gain_stage.step_value; + int totalSteps = (out->gain_stage.max_value - out->gain_stage.min_value) / + out->gain_stage.step_value; + int minDb = (out->gain_stage.min_value - VOL_DEFAULT_OFFSET) / 100; + int maxDb = (out->gain_stage.max_value - VOL_DEFAULT_OFFSET) / 100; + + // curve: 10^((minDb + (maxDb - minDb) * gainIndex / totalSteps) / 20) + // 2x10, where 10 comes from the log 10 conversion from power ratios, + // which are the square (2) of amplitude + out->amplitude_ratio = pow(10, + (minDb + (maxDb - minDb) * (gainIndex / (float)totalSteps)) / 20); + pthread_mutex_unlock(&out->lock); + ALOGD("%s: set audio gain: %f on %s", + __func__, out->amplitude_ratio, bus_address); + } else { + ALOGE("%s: can not find output stream by bus_address:%s", __func__, bus_address); + ret = -EINVAL; + } + if (bus_address) { + ALOGE("%s: free the allocated %s", __func__); + free(bus_address); + return -ENOMEM; + } + + return ret; +} + +static int adev_create_audio_patch(struct audio_hw_device *dev __unused, + unsigned int num_sources __unused, + const struct audio_port_config *sources __unused, + unsigned int num_sinks __unused, + const struct audio_port_config *sinks __unused, + audio_patch_handle_t *handle __unused) { +#if 0 + struct audio_device *audio_dev = (struct audio_device *)dev; + for (int i = 0; i < num_sources; i++) { + ALOGD("%s: source[%d] type=%d address=%s", __func__, i, sources[i].type, + sources[i].type == AUDIO_PORT_TYPE_DEVICE + ? sources[i].ext.device.address + : ""); + } + for (int i = 0; i < num_sinks; i++) { + ALOGD("%s: sink[%d] type=%d address=%s", __func__, i, sinks[i].type, + sinks[i].type == AUDIO_PORT_TYPE_DEVICE ? sinks[i].ext.device.address + : "N/A"); + } + if (num_sources == 1 && num_sinks == 1 && + sources[0].type == AUDIO_PORT_TYPE_DEVICE && + sinks[0].type == AUDIO_PORT_TYPE_DEVICE) { + pthread_mutex_lock(&audio_dev->lock); + audio_dev->last_patch_id += 1; + pthread_mutex_unlock(&audio_dev->lock); + *handle = audio_dev->last_patch_id; + } +#endif + return 0; +} + +static int adev_release_audio_patch(struct audio_hw_device *dev __unused, + audio_patch_handle_t handle) { + ALOGD("%s: handle: %d", __func__, handle); + return 0; +} + +static int adev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + ALOGV("adev_open: %s", name); + + struct audio_device *adev; + int card = 0; + char mixer_path[PATH_MAX]; + char config_path[PATH_MAX]; + char board_name[BOARD_NAME_MAX]; + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) + return -EINVAL; + + adev = calloc(1, sizeof(struct audio_device)); + if (!adev) + return -ENOMEM; + + adev->hw_device.common.tag = HARDWARE_DEVICE_TAG; + adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0; + adev->hw_device.common.module = (struct hw_module_t *) module; + adev->hw_device.common.close = adev_close; + adev->hw_device.init_check = adev_init_check; + adev->hw_device.set_voice_volume = adev_set_voice_volume; + adev->hw_device.set_master_volume = adev_set_master_volume; + adev->hw_device.get_master_volume = adev_get_master_volume; + adev->hw_device.set_master_mute = adev_set_master_mute; + adev->hw_device.get_master_mute = adev_get_master_mute; + adev->hw_device.set_mode = adev_set_mode; + adev->hw_device.set_mic_mute = adev_set_mic_mute; + adev->hw_device.get_mic_mute = adev_get_mic_mute; + adev->hw_device.set_parameters = adev_set_parameters; + adev->hw_device.get_parameters = adev_get_parameters; + adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size; + adev->hw_device.open_output_stream = adev_open_output_stream; + adev->hw_device.close_output_stream = adev_close_output_stream; + adev->hw_device.open_input_stream = adev_open_input_stream; + adev->hw_device.close_input_stream = adev_close_input_stream; + adev->hw_device.dump = adev_dump; + adev->hw_device.get_microphones = adev_get_microphones; + + adev->hal_config = audio_hal_config_init(); + if (!adev->hal_config) { + ALOGE("%s: audio hal config init fail", __func__); + free(adev); + return -EINVAL; + } + // Initialize the bus address to output stream map + adev->out_bus_stream_map = hashmapCreate(5, str_hash_fn, str_eq); + // Set max volume to the devices + memset(config_path, 0, PATH_MAX); + memset(board_name, 0, BOARD_NAME_MAX); + property_get("vendor.audio_hal.board", board_name, NULL); + if (strlen(board_name) > 0) { + snprintf(config_path, PATH_MAX, "/vendor/etc/audio_hal_configuration_%s.xml", board_name); + if (access(config_path, F_OK) == -1) { + memset(config_path, 0, PATH_MAX); + } + } + + if (strlen(config_path) == 0) { + snprintf(config_path, PATH_MAX, "/vendor/etc/audio_hal_configuration.xml"); + } + + ALOGI("%s: audio hal config file path is %s", __func__, config_path); + audio_hal_config_load_from_xml(adev->hal_config, config_path); + + adev->out_device = AUDIO_DEVICE_OUT_SPEAKER; + adev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC & ~AUDIO_DEVICE_BIT_IN; + adev->in_needs_standby = false; + adev->out_needs_standby = false; + + *device = &adev->hw_device.common; + + mixer_setting_for_volume(); + +// CLK target codec only works with sample_rate 48000, identify the target and update default pcm_config.rate if needed. + char product[PROPERTY_VALUE_MAX] = "cel_kbl"; + if(property_get("ro.hardware", product, NULL) <= 0) { + ALOGE("%s : failed to read ro.hardware", __func__); + } else { + if(strcmp(product, "clk") == 0) { + pcm_config_in.rate = 48000; + } + } + +//Update period_size based on sample rate and period_ms + size_t size = (pcm_config_in.rate * IN_PERIOD_MS * SAMPLE_SIZE_IN_BYTES_STEREO) / 1000; + pcm_config_in.period_size = size; + + ALOGI("%s : will use input [rate : period] as [%d : %u] for %s variants", __func__, pcm_config_in.rate, pcm_config_in.period_size, product); + +//[BT SCO VoIP Call + update_bt_card(adev); + + adev->in_sco_voip_call = false; + adev->is_hfp_call_active = false; + adev->voip_in_resampler = NULL; + adev->voip_out_resampler = NULL; +//BT SCO VoIP Call] + + adev->in_needs_standby = false; + adev->out_needs_standby = false; + +#ifdef DEBUG_PCM_DUMP + sco_call_write = fopen("/vendor/dump/sco_call_write.pcm", "a"); + sco_call_write_remapped = fopen("/vendor/dump/sco_call_write_remapped.pcm", "a"); + sco_call_write_bt = fopen("/vendor/dump/sco_call_write_bt.pcm", "a"); + sco_call_read = fopen("/vendor/dump/sco_call_read.pcm", "a"); + sco_call_read_remapped = fopen("/vendor/dump/sco_call_read_remapped.pcm", "a"); + sco_call_read_bt = fopen("/vendor/dump/sco_call_read_bt.pcm", "a"); + out_write_dump = fopen("/vendor/dump/out_write_dump.pcm", "a"); + in_read_dump = fopen("/vendor/dump/in_read_dump.pcm", "a"); + + if(sco_call_write == NULL || sco_call_write_remapped == NULL || sco_call_write_bt == NULL || sco_call_read == NULL || + sco_call_read_bt == NULL || sco_call_read_remapped == NULL || out_write_dump == NULL || in_read_dump == NULL) + ALOGD("%s : failed to open dump files",__func__); + else + ALOGD("%s : success in opening dump files",__func__); +#endif + + return 0; + + error: + free(adev); + return -ENODEV; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = adev_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = AUDIO_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = AUDIO_HARDWARE_MODULE_ID, + .name = "Android IA minimal HW HAL", + .author = "The Android Open Source Project", + .methods = &hal_module_methods, + }, +}; + +/* VoIP pcm write in celadon devices goes to bt alsa card */ +int out_write_bt (struct stream_out *out, struct audio_device *adev, const void* buffer, + size_t bytes) +{ + int ret = 0; + size_t frames_in = round_to_16_mult(out->pcm_config->period_size); + size_t frames_out = round_to_16_mult(bt_out_config.period_size); + size_t buf_size_out = bt_out_config.channels * frames_out * SAMPLE_SIZE_IN_BYTES; + size_t buf_size_in = out->pcm_config->channels * frames_in * SAMPLE_SIZE_IN_BYTES; + size_t buf_size_remapped = bt_out_config.channels * frames_in * SAMPLE_SIZE_IN_BYTES; + int16_t *buf_out = (int16_t *) malloc (buf_size_out); + int16_t *buf_in = (int16_t *) malloc (buf_size_in); + int16_t *buf_remapped = (int16_t *) malloc (buf_size_remapped); + + if(adev->voip_out_resampler == NULL) { + int ret = create_resampler(out->pcm_config->rate /*src rate*/, bt_out_config.rate /*dst rate*/, bt_out_config.channels/*dst channels*/, + RESAMPLER_QUALITY_DEFAULT, NULL, &(adev->voip_out_resampler)); + ALOGD("%s : frames_in %zu frames_out %zu",__func__, frames_in, frames_out); + ALOGD("%s : to write bytes : %zu", __func__, bytes); + ALOGD("%s : size_in %zu size_out %zu size_remapped %zu", __func__, buf_size_in, buf_size_out, buf_size_remapped); + + if (ret != 0) { + adev->voip_out_resampler = NULL; + ALOGE("%s : Failure to create resampler %d", __func__, ret); + + free(buf_in); + free(buf_out); + free(buf_remapped); + } else { + ALOGD("%s : voip_out_resampler created rate : [%d -> %d]", __func__, out->pcm_config->rate, bt_out_config.rate); + } + } + + memset(buf_in, 0, buf_size_in); + memset(buf_remapped, 0, buf_size_remapped); + memset(buf_out, 0, buf_size_out); + + memcpy_s(buf_in,buf_size_in, buffer, buf_size_in); + +#ifdef DEBUG_PCM_DUMP + if(sco_call_write != NULL) { + fwrite(buf_in, 1, buf_size_in, sco_call_write); + } else { + ALOGD("%s : sco_call_write was NULL, no dump", __func__); + } +#endif + + adjust_channels(buf_in, out->pcm_config->channels, buf_remapped, bt_out_config.channels, + SAMPLE_SIZE_IN_BYTES, buf_size_in); + + //ALOGV("remapping : [%d -> %d]", out->pcm_config->channels, bt_out_config.channels); + +#ifdef DEBUG_PCM_DUMP + if(sco_call_write_remapped != NULL) { + fwrite(buf_remapped, 1, buf_size_remapped, sco_call_write_remapped); + } else { + ALOGD("%s : sco_call_write_remapped was NULL, no dump", __func__); + } +#endif + + if(adev->voip_out_resampler != NULL) { + adev->voip_out_resampler->resample_from_input(adev->voip_out_resampler, (int16_t *)buf_remapped, (size_t *)&frames_in, (int16_t *) buf_out, (size_t *)&frames_out); + //ALOGV("%s : upsampling [%d -> %d]",__func__, out->pcm_config->rate, bt_out_config.rate); + } + + ALOGV("%s : modified frames_in %zu frames_out %zu",__func__, frames_in, frames_out); + + buf_size_out = bt_out_config.channels * frames_out * SAMPLE_SIZE_IN_BYTES; + bytes = out->pcm_config->channels * frames_in * SAMPLE_SIZE_IN_BYTES; + +#ifdef DEBUG_PCM_DUMP + if(sco_call_write_bt != NULL) { + fwrite(buf_out, 1, buf_size_out, sco_call_write_bt); + } else { + ALOGD("%s : sco_call_write was NULL, no dump", __func__); + } +#endif + + ret = pcm_write(out->pcm, buf_out, buf_size_out); + + free(buf_in); + free(buf_out); + free(buf_remapped); + +return ret; +} + +/* VoIP pcm read from bt alsa card */ +int in_read_bt (struct stream_in *in, struct audio_device *adev, void* buffer, + size_t bytes) +{ + int ret = 0; + size_t frames_out = round_to_16_mult(in->pcm_config->period_size); + size_t frames_in = round_to_16_mult(bt_in_config.period_size); + size_t buf_size_out = in->pcm_config->channels * frames_out * SAMPLE_SIZE_IN_BYTES; + size_t buf_size_in = bt_in_config.channels * frames_in * SAMPLE_SIZE_IN_BYTES; + size_t buf_size_remapped = in->pcm_config->channels * frames_in * SAMPLE_SIZE_IN_BYTES; + int16_t *buf_out = (int16_t *) malloc (buf_size_out); + int16_t *buf_in = (int16_t *) malloc (buf_size_in); + int16_t *buf_remapped = (int16_t *) malloc (buf_size_remapped); + + if(adev->voip_in_resampler == NULL) { + int ret = create_resampler(bt_in_config.rate /*src rate*/, in->pcm_config->rate /*dst rate*/, in->pcm_config->channels/*dst channels*/, + RESAMPLER_QUALITY_DEFAULT, NULL, &(adev->voip_in_resampler)); + ALOGV("%s : bytes_requested : %zu", __func__, bytes); + ALOGV("%s : frames_in %zu frames_out %zu",__func__, frames_in, frames_out); + ALOGD("%s : size_in %zu size_out %zu size_remapped %zu", __func__, buf_size_in, buf_size_out, buf_size_remapped); + if (ret != 0) { + adev->voip_in_resampler = NULL; + ALOGE("%s : Failure to create resampler %d", __func__, ret); + + free(buf_in); + free(buf_out); + free(buf_remapped); + } else { + ALOGD("%s : voip_in_resampler created rate : [%d -> %d]", __func__, bt_in_config.rate, in->pcm_config->rate); + } + } + + memset(buf_in, 0, buf_size_in); + memset(buf_remapped, 0, buf_size_remapped); + memset(buf_out, 0, buf_size_out); + + ret = pcm_read(in->pcm, buf_in, buf_size_in); + +#ifdef DEBUG_PCM_DUMP + if(sco_call_read != NULL) { + fwrite(buf_in, 1, buf_size_in, sco_call_read); + } else { + ALOGD("%s : sco_call_read was NULL, no dump", __func__); + } +#endif + adjust_channels(buf_in, bt_in_config.channels, buf_remapped, in->pcm_config->channels, + SAMPLE_SIZE_IN_BYTES, buf_size_in); + + //ALOGV("%s : remapping : [%d -> %d]", __func__, bt_in_config.channels, in->pcm_config->channels); + +#ifdef DEBUG_PCM_DUMP + if(sco_call_read_remapped != NULL) { + fwrite(buf_remapped, 1, buf_size_remapped, sco_call_read_remapped); + } else { + ALOGD("%s : sco_call_read_remapped was NULL, no dump", __func__); + } +#endif + + if(adev->voip_in_resampler != NULL) { + adev->voip_in_resampler->resample_from_input(adev->voip_in_resampler, (int16_t *)buf_remapped, (size_t *)&frames_in, (int16_t *) buf_out, (size_t *)&frames_out); + //ALOGV("%s : upsampling [%d -> %d]",__func__, bt_in_config.rate, in->pcm_config->rate); + } + + ALOGV("%s : modified frames_in %zu frames_out %zu",__func__, frames_in, frames_out); + + buf_size_out = in->pcm_config->channels * frames_out * SAMPLE_SIZE_IN_BYTES; + bytes = buf_size_out; + +#ifdef DEBUG_PCM_DUMP + if(sco_call_read_bt != NULL) { + fwrite(buf_out, 1, buf_size_out, sco_call_read_bt); + } else { + ALOGD("%s : sco_call_read_bt was NULL, no dump", __func__); + } +#endif + + memcpy_s(buffer,buf_size_out, buf_out, buf_size_out); + + free(buf_in); + free(buf_out); + free(buf_remapped); + +return ret; +} diff --git a/primary_new/audio_hw.h b/primary_new/audio_hw.h new file mode 100644 index 0000000..bb67692 --- /dev/null +++ b/primary_new/audio_hw.h @@ -0,0 +1,430 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +// +// All hash includes - Start +// +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +// All hash includes - End + +// +// All hash defines - Start +// +#define PCM_CARD 0 +#define PCM_CARD_DEFAULT 0 +#define PCM_DEVICE 0 + +#define OUT_PERIOD_SIZE 1024 +#define OUT_PERIOD_COUNT 4 +#define OUT_SAMPLING_RATE 48000 + +#define IN_PERIOD_SIZE 1024 // default period size +#define IN_PERIOD_MS 10 +#define IN_PERIOD_COUNT 4 +#define IN_SAMPLING_RATE 48000 +#define DEVICE_MAX_VOL 200 + +#define AUDIO_PARAMETER_HFP_ENABLE "hfp_enable" +#define AUDIO_PARAMETER_BT_SCO "BT_SCO" +#define AUDIO_BT_DRIVER_NAME "btaudiosource" +#define SAMPLE_SIZE_IN_BYTES 2 +#define SAMPLE_SIZE_IN_BYTES_STEREO 4 + +#define PCM_DUMMY_DEVICE 0 + +#define BOARD_NAME_MAX 128 +#define INPUT_BUFFER_MILLISECONDS 20 + +#define VOL_DEFAULT_MIN_VALUE -3200 +#define VOL_DEFAULT_MAX_VALUE 600 +#define VOL_DEFAULT_STEP_VALUE 100 +#define VOL_DEFAULT_GAIN 0.5 +#define VOL_DEFAULT_OFFSET VOL_DEFAULT_MAX_VALUE + +// All hash defines - End + +// +// All Struct defines - Start +// + +struct pcm_config pcm_config_out = { + .channels = 2, + .rate = OUT_SAMPLING_RATE, + .period_size = OUT_PERIOD_SIZE, + .period_count = OUT_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = OUT_PERIOD_SIZE * OUT_PERIOD_COUNT, +}; + +struct pcm_config pcm_config_in = { + .channels = 2, + .rate = IN_SAMPLING_RATE, + .period_size = IN_PERIOD_SIZE, + .period_count = IN_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 1, + .stop_threshold = (IN_PERIOD_SIZE * IN_PERIOD_COUNT), +}; + +//[ BT ALSA Card config +struct pcm_config bt_out_config = {.channels = 1, + .rate = 8000, + .period_size = 240, + .period_count = 5, + .start_threshold = 0, + .stop_threshold = 0, + .silence_threshold = 0, + .silence_size = 0, + .avail_min = 0}; + +struct pcm_config bt_in_config = {.channels = 1, + .rate = 8000, + .period_size = 240, + .period_count = 5, + .start_threshold = 0, + .stop_threshold = 0, + .silence_threshold = 0, + .silence_size = 0, + .avail_min = 0}; + +struct audio_device { + struct audio_hw_device hw_device; + + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + unsigned int out_device; + unsigned int in_device; + bool standby; + bool mic_mute; + struct audio_route *ar; + void *hal_config; + + int card; + int cardc; + struct stream_out *active_out; + struct stream_in *active_in; + + //[BT-HFP Voice Call + bool is_hfp_call_active; + // BT-HFP Voice Call] + + bool in_needs_standby; + bool out_needs_standby; + + //[BT SCO VoIP Call + bool in_sco_voip_call; + int bt_card; + struct resampler_itfe *voip_in_resampler; + struct resampler_itfe *voip_out_resampler; + // BT SCO VoIP Call] + + Hashmap *out_bus_stream_map; // Extended field. Constant after init +}; + +enum output_channel_enable { + LEFT_CHANNEL = 1, + RIGHT_CHANNEL = 1 << 1, + BOTH_CHANNELS = LEFT_CHANNEL | RIGHT_CHANNEL +}; + +struct stream_out { + struct audio_stream_out stream; + + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + struct pcm *pcm; + struct pcm_config *pcm_config; + struct audio_config req_config; + bool unavailable; + bool standby; + uint64_t written; + struct audio_device *dev; + struct audio_route *ar; + char *bus_address; + int card; + int device; + struct resampler_itfe *resampler; + + enum output_channel_enable enabled_channels; // Constant after init + float amplitude_ratio; // Protected by this->lock + struct audio_gain gain_stage; // Constant after init + bool apply_sw_volume; + + struct pcm_config *stream_config; + int stream_card; + int stream_device; + bool stream_virtio_card; +}; + +struct stream_in { + struct audio_stream_in stream; + + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + struct pcm *pcm; + struct pcm_config *pcm_config; + struct audio_config req_config; + bool unavailable; + bool standby; + uint64_t frames_read; + struct audio_device *dev; + struct audio_route *ar; + char *bus_address; + int card; + int device; + struct resampler_itfe *resampler; + + struct pcm_config *stream_config; + int stream_card; + int stream_device; + +}; + +struct pcm_config dummy_pcm_config_out = { + .channels = 2, + .rate = OUT_SAMPLING_RATE, + .period_size = OUT_PERIOD_SIZE, + .period_count = OUT_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = OUT_PERIOD_SIZE * OUT_PERIOD_COUNT, +}; + +struct pcm_config dummy_pcm_config_in = { + .channels = 2, + .rate = IN_SAMPLING_RATE, + .period_size = IN_PERIOD_SIZE, + .period_count = IN_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 1, + .stop_threshold = (IN_PERIOD_SIZE * IN_PERIOD_COUNT), +}; + +// All Struct defines - End + +// +// All Function declarations - Start +// +static uint32_t out_get_sample_rate(const struct audio_stream *stream); +static size_t out_get_buffer_size(const struct audio_stream *stream); +static audio_format_t out_get_format(const struct audio_stream *stream); +static uint32_t in_get_sample_rate(const struct audio_stream *stream); +static size_t in_get_buffer_size(const struct audio_stream *stream); +static audio_format_t in_get_format(const struct audio_stream *stream); +static void adev_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream); +static void adev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream); + +static unsigned int round_to_16_mult(unsigned int size) { + return (size + 15) & ~15; /* 0xFFFFFFF0; */ +} + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) { + struct stream_out *out = (struct stream_out *)stream; + ALOGV("%s : rate %d", __func__, out->req_config.sample_rate); + return out->req_config.sample_rate; +} + +static int out_set_sample_rate(struct audio_stream *stream __unused, + uint32_t rate __unused) { + ALOGV("out_set_sample_rate: %d", rate); + return -ENOSYS; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) { + ALOGV("out_get_buffer_size"); + return pcm_config_out.period_size * + audio_stream_out_frame_size((struct audio_stream_out *)stream); +} + +static uint32_t out_get_channels(const struct audio_stream *stream) { + struct stream_out *out = (struct stream_out *)stream; + ALOGV("%s : channels %d", __func__, popcount(out->req_config.channel_mask)); + return out->req_config.channel_mask; +} + +static audio_format_t out_get_format(const struct audio_stream *stream) { + ALOGV("%s", __func__); + struct stream_out *out = (struct stream_out *)stream; + return out->req_config.format; +} + +static int out_set_format(struct audio_stream *stream __unused, + audio_format_t format __unused) { + return -ENOSYS; +} + +static int out_dump(const struct audio_stream *stream __unused, + int fd __unused) { + ALOGV("out_dump"); + return 0; +} + +static uint32_t +out_get_latency(const struct audio_stream_out *stream __unused) { + ALOGV("out_get_latency"); + return (pcm_config_out.period_size * OUT_PERIOD_COUNT * 1000) / + pcm_config_out.rate; +} + +static int out_set_volume(struct audio_stream_out *stream __unused, + float left __unused, float right __unused) { + ALOGV("out_set_volume: Left:%f Right:%f", left, right); + return -ENOSYS; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) { + struct stream_out *out = (struct stream_out *)stream; + *dsp_frames = out->written; + ALOGV("%s : dsp_frames: %d", __func__, *dsp_frames); + return 0; +} + +static int out_get_presentation_position(const struct audio_stream_out *stream, + uint64_t *frames, + struct timespec *timestamp) { + struct stream_out *out = (struct stream_out *)stream; + int ret = -1; + + if (out->pcm) { + unsigned int avail; + if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) { + unsigned int kernel_buffer_size = + out->pcm_config->period_size * out->pcm_config->period_count; + int64_t signed_frames = out->written - kernel_buffer_size + avail; + if (signed_frames >= 0) { + *frames = signed_frames; + ret = 0; + } + } + } + + return ret; +} + +static int out_add_audio_effect(const struct audio_stream *stream __unused, + effect_handle_t effect __unused) { + ALOGV("out_add_audio_effect: %p", effect); + return 0; +} + +static int out_remove_audio_effect(const struct audio_stream *stream __unused, + effect_handle_t effect __unused) { + ALOGV("out_remove_audio_effect: %p", effect); + return 0; +} + +static int +out_get_next_write_timestamp(const struct audio_stream_out *stream __unused, + int64_t *timestamp __unused) { + ALOGV("%s", __func__); + return -ENOSYS; +} + +static uint32_t in_get_sample_rate(const struct audio_stream *stream) { + struct stream_in *in = (struct stream_in *)stream; + ALOGV("%s : req_config %d", __func__, in->req_config.sample_rate); + return in->req_config.sample_rate; +} + +static int in_set_sample_rate(struct audio_stream *stream __unused, + uint32_t rate __unused) { + ALOGV("in_set_sample_rate: %d", rate); + return -ENOSYS; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) { + struct stream_in *in = (struct stream_in *)stream; + size_t size; + + /* + * take resampling into account and return the closest majoring + * multiple of 16 frames, as audioflinger expects audio buffers to + * be a multiple of 16 frames + */ + size = (in->pcm_config->period_size * in_get_sample_rate(stream)) / + in->pcm_config->rate; + size = ((size + 15) / 16) * 16; + + size *= audio_stream_in_frame_size(&in->stream); + ALOGV("%s : buffer_size : %zu", __func__, size); + return size; +} + +static uint32_t in_get_channels(const struct audio_stream *stream) { + struct stream_in *in = (struct stream_in *)stream; + + ALOGV("%s : channels %d", __func__, popcount(in->req_config.channel_mask)); + return in->req_config.channel_mask; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) { + struct stream_in *in = (struct stream_in *)stream; + ALOGV("%s : req_config format %d", __func__, in->req_config.format); + return in->req_config.format; +} + +static int in_set_format(struct audio_stream *stream __unused, + audio_format_t format __unused) { + return -ENOSYS; +} + +static int in_dump(const struct audio_stream *stream __unused, + int fd __unused) { + return 0; +} + +static int in_set_gain(struct audio_stream_in *stream __unused, + float gain __unused) { + return 0; +} + +static uint32_t +in_get_input_frames_lost(struct audio_stream_in *stream __unused) { + return 0; +} + +static int in_add_audio_effect(const struct audio_stream *stream __unused, + effect_handle_t effect __unused) { + return 0; +} + +static int in_remove_audio_effect(const struct audio_stream *stream __unused, + effect_handle_t effect __unused) { + return 0; +} + +int in_read_bt(struct stream_in *in, struct audio_device *adev, void *buffer, + size_t bytes); +int out_write_bt(struct stream_out *out, struct audio_device *adev, + const void *buffer, size_t bytes); + +// All Function declarations - End diff --git a/primary_new/config.c b/primary_new/config.c new file mode 100644 index 0000000..0b2fdf1 --- /dev/null +++ b/primary_new/config.c @@ -0,0 +1,698 @@ +//Copyright (C) 2024 Intel Corporation +//SPDX-License-Identifier: Apache-2.0 + +#define LOG_TAG "audio_hal_config" +/*#define LOG_NDEBUG 0*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "config.h" + +#define TAG_STREAM "Stream" +#define TAG_PCM "Pcm" +#define TAG_MIXER "Mixer" + +#define ATTR_ADDRESS "Address" +#define ATTR_DIRECTION "Direction" +#define ATTR_CARD "Card" +#define ATTR_DEVICE "Device" +#define ATTR_MMAP "Mmap" +#define ATTR_DATADUMP "DataDump" +#define ATTR_SAMPLE_RATE "SampleRate" +#define ATTR_FORMAT "Format" +#define ATTR_CHANNELS "Channels" +#define ATTR_PERIOD_SIZE "PeriodSize" +#define ATTR_PERIOD_COUNT "PeriodCount" +#define ATTR_START_THRESHOLD "StartThreshhold" +#define ATTR_STOP_THRESHOLD "StopThreshold" +#define ATTR_ADDITIONAL_OUT_DELAY "AdditionalOutputDeviceDelay" +#define ATTR_AVAIL_MIN "AvailMin" +#define ATTR_MIXER_PATH "MixerPath" + +#define HASHMAP_INIT 5 +#define BUF_SIZE 1024 +#define ADDRESS_LENGTH 50 +#define CARD_NAME_LENGTH 50 +#define MIXER_PATH_LENGTH 100 + +#define DIRECTION_PLAYBACK "playback" +#define DIRECTION_CAPTURE "capture" + +enum Level { + ROOT, + STREAM, +}; + +struct config_parse_state { + struct audio_hal_config *config; + + enum Level level; + void* context; /* temp solution, maybe request improvement */ +}; + +struct audio_hal_config { + Hashmap *playback; + Hashmap *capture; +}; + +/* copied from libcutils/str_parms.c */ +static bool str_eq(void *key_a, void *key_b) { + return !strcmp((const char *)key_a, (const char *)key_b); +} +static int str_hash_fn(void *str) { + uint32_t hash = 5381; + char *p; + for (p = str; p && *p; p++) { + hash = ((hash << 5) + hash) + *p; + } + return (int)hash; +} + +static bool print_stream_config(void *key __unused, void *value, void *context __unused) { + struct stream_config* sc = (struct stream_config*)value; + int bit = 0; + + switch (sc->pcm_config.format) { + case PCM_FORMAT_S16_LE: + bit = 16; + break; + case PCM_FORMAT_S24_LE: + bit = 24; + break; + case PCM_FORMAT_S32_LE: + bit = 32; + default: + break; + } + + ALOGI("address=%s, card=%s, device=%d", sc->address, sc->card_name, sc->device_id); + ALOGI("rate=%d, channel=%d, bit=%d", sc->pcm_config.rate, sc->pcm_config.channels, bit); + ALOGI("period_count=%d, period_size=%d, start_threshold=%d, stop_threshold=%d, avail_min=%d", + sc->pcm_config.period_size, sc->pcm_config.period_count, + sc->pcm_config.start_threshold, sc->pcm_config.stop_threshold, sc->pcm_config.avail_min); + if (sc->mixer_path) { + ALOGI("mixer card=%s, mixer_path=%s", sc->mixer_path->card_name, sc->mixer_path->mixer_path); + } + + return true; +} + +static void parse_mixer_tag(struct stream_config* config, const XML_Char **attr) +{ + const XML_Char *attr_value = NULL; + unsigned int i; + char *end = NULL; + long value = -1; + char *card_name = NULL, *mixer_path = NULL; + + for (i = 0; attr[i]; i += 2) { + attr_value = attr[i + 1]; + if (attr_value == NULL || strlen(attr_value) == 0) { + ALOGE("%s: attribute value is NULL for mixer", __func__); + free(mixer_path); + free(card_name); + return; + } + ALOGV("%s,%s: key=%s, value=%s", __func__, config->address, attr[i], attr[i + 1]); + + if (!strcmp(attr[i], ATTR_CARD)) { + card_name = (char *)calloc(CARD_NAME_LENGTH, sizeof(char)); + if (!card_name) { + ALOGE("%s,%s: alloc fail, no memory, %s=%s", __func__, config->address, attr[i], attr[i + 1]); + if (mixer_path) { + free(mixer_path); + mixer_path = NULL; + free(card_name); + } + return; + } + memcpy(card_name, attr_value, CARD_NAME_LENGTH); + } else if (!strcmp(attr[i], ATTR_MIXER_PATH)) { + mixer_path = (char *)calloc(MIXER_PATH_LENGTH, sizeof(char)); + if (!mixer_path) { + ALOGE("%s,%s: alloc fail, no memory, %s=%s", __func__, config->address, attr[i], attr[i + 1]); + if (card_name) { + free(card_name); + card_name = NULL; + } + free(mixer_path); + return; + } + memcpy(mixer_path, attr_value, MIXER_PATH_LENGTH); + } + } + + if (!card_name || !mixer_path) { + ALOGE("%s,%s: incorrect mixer path setting", __func__, config->address); + if (mixer_path) { + free(mixer_path); + mixer_path = NULL; + } + if (card_name) { + free(card_name); + card_name = NULL; + } + return; + } + + config->mixer_path = (struct mixer_path_config *)calloc(MIXER_PATH_LENGTH, + sizeof(struct mixer_path_config)); + if (!config->mixer_path) { + ALOGE("%s,%s: alloc mixer path fail, no memory", __func__, config->address); + free(card_name); + free(mixer_path); + card_name = NULL; + mixer_path = NULL; + return; + } + + config->mixer_path->card_name = card_name; + config->mixer_path->mixer_path = mixer_path; + + return; +} + +static void parse_pcm_tag(struct stream_config* config, const XML_Char **attr) +{ + const XML_Char *attr_value = NULL; + unsigned int i; + char *end = NULL; + long value = -1; + char *card_name = NULL; + int device = -1, format = -1, channels = -1, rate = -1; + int period_size = -1, period_count = -1; + int start_threshold = -1, stop_threshold = -1, avail_min = -1; + int additional_out_delay = 0; + + for (i = 0; attr[i]; i += 2) { + attr_value = attr[i + 1]; + if (attr_value == NULL || strlen(attr_value) == 0) { + ALOGE("%s,%s: attribute value is NULL for pcm config", __func__, config->address); + if (card_name) { + free(card_name); + card_name = NULL; + } + return; + } + ALOGV("%s,%s: key=%s, value=%s", __func__, config->address, attr[i], attr[i + 1]); + + if (!strcmp(attr[i], ATTR_CARD)) { + card_name = (char *)calloc(CARD_NAME_LENGTH, sizeof(char)); + if (!card_name) { + ALOGE("%s,%s: alloc fail, no memory, %s=%s", __func__, config->address, attr[i], attr[i + 1]); + free(card_name); + return; + } + memcpy(card_name, attr_value, CARD_NAME_LENGTH); + continue; + } + + value = strtol((char *)attr_value, &end, 0); + if (end == (char *)attr_value) { + ALOGE("%s,%s: not correct setting for pcm config, %s", __func__, config->address, attr[i]); + if (card_name) { + free(card_name); + card_name = NULL; + } + return; + } + + if (!strcmp(attr[i], ATTR_DEVICE)) { + device = (value >= 0 ? value : -1); + } else if (!strcmp(attr[i], ATTR_SAMPLE_RATE)) { + if (value != 8000 && value != 16000 && value != 32000 + && value != 44100 && value != 48000) { + ALOGE("%s,%s: not supported pcm sample rate, %ld", __func__, config->address, value); + if (card_name) { + free(card_name); + card_name = NULL; + } + return; + } + rate = value; + } else if (!strcmp(attr[i], ATTR_FORMAT)) { + switch (value) + { + case 16: + format = PCM_FORMAT_S16_LE; + break; + case 24: + format = PCM_FORMAT_S24_LE; + break; + case 32: + format = PCM_FORMAT_S32_LE; + break; + default: + ALOGE("%s,%s: not supported pcm format, %ld", __func__, config->address, value); + if (card_name) { + free(card_name); + card_name = NULL; + } + return; + } + } else if (!strcmp(attr[i], ATTR_CHANNELS)) { + if (value != 1 && value != 2 && value != 4 && value != 6 + && value != 8 && value != 12) { + ALOGE("%s,%s: not supported pcm channels, %ld", __func__, config->address, value); + if (card_name) { + free(card_name); + card_name = NULL; + } + return; + } + channels = value; + } else if (!strcmp(attr[i], ATTR_PERIOD_SIZE)) { + period_size = value; + } else if (!strcmp(attr[i], ATTR_PERIOD_COUNT)) { + period_count = value; + } else if (!strcmp(attr[i], ATTR_START_THRESHOLD)) { + start_threshold = value; + } else if (!strcmp(attr[i], ATTR_STOP_THRESHOLD)) { + stop_threshold = value; + } else if (!strcmp(attr[i], ATTR_AVAIL_MIN)) { + avail_min = value; + } else if (!strcmp(attr[i], ATTR_ADDITIONAL_OUT_DELAY)) { + additional_out_delay = value; + } + + end = NULL; + value = -1; + } + + if (!card_name || device < 0 || format < 0 || channels < 0 || rate < 0) { + ALOGE("%s,%s: incorrect card, device, format, channels or rate", __func__, config->address); + if (card_name) { + free(card_name); + card_name = NULL; + } + return; + } + + config->card_name = card_name; + config->device_id = device; + config->additional_out_delay = additional_out_delay; + config->pcm_config.format = format; + config->pcm_config.channels = channels; + config->pcm_config.rate = rate; + config->pcm_config.period_size = (period_size >= 0 ? period_size : 0); + config->pcm_config.period_count = (period_count >= 0 ? period_count : 0); + config->pcm_config.start_threshold = (start_threshold >= 0 ? start_threshold : 0); + config->pcm_config.stop_threshold = (stop_threshold >= 0 ? stop_threshold : 0); + config->pcm_config.avail_min = avail_min; +} + +static struct stream_config *parse_stream_tag(struct audio_hal_config *config, const XML_Char **attr) +{ + const XML_Char *attr_value = NULL; + struct stream_config* sc = NULL; + char* address = NULL; + Hashmap** hashmap = NULL; + bool isMmap = false, isDataDump = false; + unsigned int i; + + for (i = 0; attr[i]; i += 2) { + attr_value = attr[i + 1]; + if (attr_value == NULL || strlen(attr_value) == 0) { + ALOGE("%s: attribute value is NULL for stream", __func__); + free((void *)attr_value); + return NULL; + } + + ALOGV("%s, key=%s, value=%s", __func__, attr[i], attr[i + 1]); + + if (strcmp(attr[i], ATTR_ADDRESS) == 0) { + address = (char *)calloc(ADDRESS_LENGTH, sizeof(char)); + if (!address) { + ALOGE("%s: address alloc fail, no memory", __func__); + free((void *)attr_value); + return NULL; + } + + memcpy(address, attr_value, ADDRESS_LENGTH); + } else if (strcmp(attr[i], ATTR_DIRECTION) == 0) { + if (!strcmp((char *)attr_value, DIRECTION_PLAYBACK)) { + + hashmap = &config->playback; + } else if (!strcmp((char *)attr_value, DIRECTION_CAPTURE) ) { + hashmap = &config->capture; + } else { + ALOGE("%s: not correct direction setting for stream", __func__); + if (!address) { + free(address); + } + free((void *)attr_value); + return NULL; + } + } else if (!strcmp(attr[i], ATTR_MMAP) || !strcmp(attr[i], ATTR_DATADUMP) ) { + if (strcmp((char *)attr_value, "true") || strcmp((char *)attr_value, "True")) { + if (!strcmp(attr[i], "Mmap")) { + isMmap = true; + } else { + isDataDump = true; + } + } + } + } + + if (!address || !hashmap) { + ALOGE("%s: incorrect stream setting", __func__); + + if (!address) { + free(address); + } + + free((void *)attr_value); + return NULL; + } + + sc = (struct stream_config *)calloc(1, sizeof(struct stream_config)); + if (!sc) { + free((void *)attr_value); + ALOGE("%s: no memory to alloc stream_config", __func__); + return NULL; + } + + sc->address = address; + sc->card_name = NULL; + sc->device_id = -1; + sc->mmap = isMmap; + sc->pcm_dump = isDataDump; + hashmapPut(*hashmap, sc->address, sc); + + return sc; +} + +static void start_tag(void *data, const XML_Char *tag_name, + const XML_Char **attr) +{ + struct config_parse_state *state = data; + + if (!strcmp(tag_name, TAG_STREAM)) { + struct stream_config *sc = parse_stream_tag(state->config, attr); + if (sc == NULL) { + ALOGE("%s: parse stream tag wrong", __func__); + return; + } + + state->level = STREAM; + state->context = (void*)sc; + } else if (!strcmp(tag_name, TAG_PCM)) { + struct stream_config *sc = NULL; + + if (state->level != STREAM) { + ALOGE("%s: abnormal parse state for pcm config", __func__); + return; + } + + sc = state->context; + if (sc->card_name != NULL || sc->device_id > 0) { + ALOGE("%s: pcm config already get, %s", __func__, sc->address); + return; + } + + parse_pcm_tag(sc, attr); + } else if (!strcmp(tag_name, TAG_MIXER)) { + struct stream_config *sc = NULL; + + if (state->level != STREAM) { + ALOGE("%s: abnormal parse state for mixer config", __func__); + return; + } + + sc = state->context; + parse_mixer_tag(sc, attr); + } +} + +static void end_tag(void *data, const XML_Char *tag_name) +{ + struct config_parse_state *state = data; + + if (!strcmp(tag_name, TAG_STREAM)) { + state->level = ROOT; + state->context = NULL; + } +} + +void* audio_hal_config_init(void) +{ + struct audio_hal_config *hal_config; + + hal_config = calloc(1, sizeof(struct audio_hal_config)); + if (!hal_config) { + ALOGE("%s: memory allocate fail", __func__); + return NULL; + } + + hal_config->playback = hashmapCreate(HASHMAP_INIT, str_hash_fn, str_eq); + hal_config->capture = hashmapCreate(HASHMAP_INIT, str_hash_fn, str_eq); + + return (void*)hal_config; +} + +int audio_hal_config_load_from_xml(void* handle, const char *xml_path) +{ + struct config_parse_state state; + XML_Parser parser; + FILE *file; + int bytes_read; + void *buf; + struct audio_hal_config *hal_config = (struct audio_hal_config *)handle; + + if (!hal_config) { + ALOGE("%s: invalid handle", __func__); + return -1; + } + + if (!xml_path) { + ALOGE("%s: invalid xml path", __func__); + return -1; + } + + file = fopen(xml_path, "r"); + if (!file) { + ALOGE("Failed to open %s: %s", xml_path, strerror(errno)); + return -1; + } + + parser = XML_ParserCreate(NULL); + if (!parser) { + ALOGE("Failed to create XML parser"); + fclose(file); + return -1; + } + + memset(&state, 0, sizeof(state)); + state.config = hal_config; + state.level = ROOT; + XML_SetUserData(parser, &state); + XML_SetElementHandler(parser, start_tag, end_tag); + + for (;;) { + buf = XML_GetBuffer(parser, BUF_SIZE); + if (buf == NULL) { + XML_ParserFree(parser); + fclose(file); + return -1; + } + + bytes_read = fread(buf, 1, BUF_SIZE, file); + if (bytes_read < 0) { + XML_ParserFree(parser); + fclose(file); + return -1; + } + + if (XML_ParseBuffer(parser, bytes_read, + bytes_read == 0) == XML_STATUS_ERROR) { + ALOGE("Error parse audio hal config xml (%s)", xml_path); + + XML_ParserFree(parser); + fclose(file); + return -1; + } + + if (bytes_read == 0) { + break; + } + } + + XML_ParserFree(parser); + fclose(file); + + char debug[PROPERTY_VALUE_MAX] = "false"; + property_get("vendor.audio_config.debug", debug, NULL); + if (!strcmp(debug,"true")) { + ALOGI("Playback stream config:"); + hashmapForEach(hal_config->playback, print_stream_config, NULL); + + ALOGI("Capture stream config:"); + hashmapForEach(hal_config->capture, print_stream_config, NULL); + } + + return 0; +} + +static bool free_stream_config(void *key, void *value, void *context) +{ + Hashmap *map = (Hashmap*)context; + struct stream_config* sc = (struct stream_config*)value; + + ALOGI("%s: %s", __func__, sc->address); + + hashmapRemove(map, key); + + if (sc->mixer_path) { + free(sc->mixer_path->card_name); + free(sc->mixer_path->mixer_path); + } + + free(sc->card_name); + free(sc->address); + free(sc); + + return true; +} + +void audio_hal_config_free(void* handle) +{ + struct audio_hal_config* config = (struct audio_hal_config*)handle; + + if (config != NULL) { + hashmapForEach(config->playback, free_stream_config, config->playback); + hashmapFree(config->playback); + + hashmapForEach(config->capture, free_stream_config, config->capture); + hashmapFree(config->capture); + + free(config); + config = NULL; + } +} + +struct stream_config *audio_hal_config_get(void* handle, const char* address, bool playback) +{ + struct audio_hal_config* config = NULL; + Hashmap *map = NULL; + + if (handle == NULL || address == NULL) { + ALOGE("%s: incorrect argument", __func__); + return 0; + } + + config = (struct audio_hal_config*)handle; + map = playback ? config->playback : config->capture; + + return hashmapGet(map, (void*)address); +} + +int audio_hal_config_add(void* handle, struct stream_config *item, bool playback) +{ + struct audio_hal_config* config = NULL; + struct stream_config *sc = NULL; + Hashmap *map = NULL; + + if (!handle || !item->address || !item->card_name) { + ALOGE("%s: incorrect argument", __func__); + return -1; + } + + config = (struct audio_hal_config*)handle; + map = playback ? config->playback : config->capture; + + sc = hashmapGet(map, (void*)item->address); + if (sc) { + ALOGE("%s: config item already exist", __func__); + return 0; + } + + sc = (struct stream_config *)calloc(1, sizeof(struct stream_config)); + if (!sc) { + ALOGE("%s: no memory to alloc stream_config", __func__); + return -1; + } + sc->address = (char *)calloc(1, strlen(item->address)+1); + if (!sc->address) { + ALOGE("%s: no memory to alloc stream address", __func__); + free(sc); + return -1; + } + sc->card_name = (char *)calloc(1, strlen(item->card_name)+1); + if (!sc->card_name) { + ALOGE("%s: no memory to alloc stream card name", __func__); + free(sc->address); + free(sc); + return -1; + } + + memcpy(sc, item, sizeof(struct stream_config)); + memcpy(sc->address, item->address, strlen(item->address)); + memcpy(sc->card_name, item->card_name, strlen(item->card_name)); + + if (item->mixer_path) { + sc->mixer_path = (struct mixer_path_config *)calloc(1, sizeof(struct mixer_path_config)); + if (!sc->mixer_path) { + ALOGE("%s: no memory to alloc mixer", __func__); + free(sc->card_name); + free(sc->address); + free(sc); + return -1; + } + sc->mixer_path->card_name = (char *)calloc(1, strlen(item->mixer_path->card_name)+1); + if (!sc->mixer_path->card_name) { + ALOGE("%s: no memory to alloc mixer card name", __func__); + free(sc->mixer_path); + free(sc->card_name); + free(sc->address); + free(sc); + return -1; + } + sc->mixer_path->mixer_path = (char *)calloc(1, strlen(item->mixer_path->mixer_path)+1); + if (!sc->mixer_path->mixer_path) { + ALOGE("%s: no memory to alloc mixer path", __func__); + free(sc->mixer_path->card_name); + free(sc->mixer_path); + free(sc->card_name); + free(sc->address); + free(sc); + return -1; + } + memcpy(sc->mixer_path->card_name, item->mixer_path->card_name, strlen(item->mixer_path->card_name)); + memcpy(sc->mixer_path->mixer_path, item->mixer_path->mixer_path, strlen(item->mixer_path->mixer_path)); + } + strcat(sc->address, "\0"); + hashmapPut(map, sc->address, sc); + + return 0; +} + +int audio_hal_config_delete(void* handle, const char* address, bool playback) +{ + struct audio_hal_config* config = NULL; + struct stream_config *item = NULL; + Hashmap *map = NULL; + + if (handle == NULL || address == NULL) { + ALOGE("%s: incorrect argument", __func__); + return -1; + } + + config = (struct audio_hal_config*)handle; + map = playback ? config->playback : config->capture; + + item = hashmapGet(map, (void*)address); + if (item != NULL) { + hashmapRemove(map, (void*)address); + return -1; + } + + return 0; +} diff --git a/primary_new/config.h b/primary_new/config.h new file mode 100644 index 0000000..7f31126 --- /dev/null +++ b/primary_new/config.h @@ -0,0 +1,42 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#ifndef AUDIO_HAL_CONFIG_H +#define AUDIO_HAL_CONFIG_H + +#if defined(__cplusplus) +extern "C" { +#endif + +struct mixer_path_config { + char *card_name; + char *mixer_path; +}; + +struct stream_config { + char *address; + char *card_name; + int device_id; + bool mmap; + bool pcm_dump; + int additional_out_delay; + struct pcm_config pcm_config; + struct mixer_path_config *mixer_path; +}; + +void *audio_hal_config_init(void); +void audio_hal_config_free(void *handle); + +int audio_hal_config_load_from_xml(void *handle, const char *xml_path); +int audio_hal_config_add(void *handle, struct stream_config *item, + bool playback); +int audio_hal_config_delete(void *handle, const char *address, bool playback); + +struct stream_config *audio_hal_config_get(void *handle, const char *address, + bool playback); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif diff --git a/primary_new/utils.c b/primary_new/utils.c new file mode 100644 index 0000000..6547856 --- /dev/null +++ b/primary_new/utils.c @@ -0,0 +1,41 @@ +//Copyright (C) 2024 Intel Corporation +//SPDX-License-Identifier: Apache-2.0 + +#define LOG_TAG "audio_hal_utils" +/*#define LOG_NDEBUG 0*/ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "utils.h" + +bool check_virito_card(int card) +{ + int snd_card; + char id_filepath[PATH_MAX] = {0}; + struct snd_ctl_card_info card_info; + + snprintf(id_filepath, sizeof(id_filepath),"/dev/snd/controlC%d", card); + snd_card = open(id_filepath, O_RDONLY); + if (snd_card < 0 || ioctl(snd_card, SNDRV_CTL_IOCTL_CARD_INFO, &card_info) < 0) { + ALOGE("%s: Get info from card%d failed", __func__, card); + return false; + } + + if (!strcmp((char*)card_info.driver, "virtio-snd")) { + close(snd_card); + + return true; + } + close(snd_card); + + return false; +} + diff --git a/primary_new/utils.h b/primary_new/utils.h new file mode 100644 index 0000000..364a3b6 --- /dev/null +++ b/primary_new/utils.h @@ -0,0 +1,17 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#ifndef AUDIO_HAL_UTILS_H +#define AUDIO_HAL_UTILS_H + +#if defined(__cplusplus) +extern "C" { +#endif + +bool check_virito_card(int card); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif