blob: fae3e00b4b5ecae7fff31b38ce9bcf400e5bedd0 [file] [log] [blame]
/*
* Copyright (C) 2011 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"
/*#define LOG_NDEBUG 0*/
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <sys/time.h>
#include <stdlib.h>
#include <cutils/log.h>
#include <cutils/str_parms.h>
#include <cutils/properties.h>
#include <hardware/hardware.h>
#include <system/audio.h>
#include <hardware/audio.h>
#include <tinyalsa/asoundlib.h>
#include <audio_utils/resampler.h>
#include <audio_utils/echo_reference.h>
#include <hardware/audio_effect.h>
#include <audio_effects/effect_aec.h>
#include "ril_interface.h"
/* Mixer control names */
#define MIXER_DL2_LEFT_EQUALIZER "DL2 Left Equalizer"
#define MIXER_DL2_RIGHT_EQUALIZER "DL2 Right Equalizer"
#define MIXER_DL1_MEDIA_PLAYBACK_VOLUME "DL1 Media Playback Volume"
#define MIXER_DL1_VOICE_PLAYBACK_VOLUME "DL1 Voice Playback Volume"
#define MIXER_DL2_MEDIA_PLAYBACK_VOLUME "DL2 Media Playback Volume"
#define MIXER_DL2_VOICE_PLAYBACK_VOLUME "DL2 Voice Playback Volume"
#define MIXER_SDT_DL_VOLUME "SDT DL Volume"
#define MIXER_SDT_UL_VOLUME "SDT UL Volume"
#define MIXER_HEADSET_PLAYBACK_VOLUME "Headset Playback Volume"
#define MIXER_HANDSFREE_PLAYBACK_VOLUME "Handsfree Playback Volume"
#define MIXER_EARPHONE_PLAYBACK_VOLUME "Earphone Playback Volume"
#define MIXER_BT_UL_VOLUME "BT UL Volume"
#define MIXER_DL1_MIXER_MULTIMEDIA "DL1 Mixer Multimedia"
#define MIXER_DL1_MIXER_VOICE "DL1 Mixer Voice"
#define MIXER_DL2_MIXER_MULTIMEDIA "DL2 Mixer Multimedia"
#define MIXER_DL2_MIXER_VOICE "DL2 Mixer Voice"
#define MIXER_SIDETONE_MIXER_PLAYBACK "Sidetone Mixer Playback"
#define MIXER_SIDETONE_MIXER_CAPTURE "Sidetone Mixer Capture"
#define MIXER_DL1_PDM_SWITCH "DL1 PDM Switch"
#define MIXER_DL1_BT_VX_SWITCH "DL1 BT_VX Switch"
#define MIXER_VOICE_CAPTURE_MIXER_CAPTURE "Voice Capture Mixer Capture"
#define MIXER_HS_LEFT_PLAYBACK "HS Left Playback"
#define MIXER_HS_RIGHT_PLAYBACK "HS Right Playback"
#define MIXER_HF_LEFT_PLAYBACK "HF Left Playback"
#define MIXER_HF_RIGHT_PLAYBACK "HF Right Playback"
#define MIXER_EARPHONE_ENABLE_SWITCH "Earphone Enable Switch"
#define MIXER_ANALOG_LEFT_CAPTURE_ROUTE "Analog Left Capture Route"
#define MIXER_ANALOG_RIGHT_CAPTURE_ROUTE "Analog Right Capture Route"
#define MIXER_CAPTURE_PREAMPLIFIER_VOLUME "Capture Preamplifier Volume"
#define MIXER_CAPTURE_VOLUME "Capture Volume"
#define MIXER_AMIC_UL_VOLUME "AMIC UL Volume"
#define MIXER_AUDUL_VOICE_UL_VOLUME "AUDUL Voice UL Volume"
#define MIXER_MUX_VX0 "MUX_VX0"
#define MIXER_MUX_VX1 "MUX_VX1"
#define MIXER_MUX_UL10 "MUX_UL10"
#define MIXER_MUX_UL11 "MUX_UL11"
/* Mixer control gain and route values */
#define MIXER_ABE_GAIN_0DB 120
#define MIXER_PLAYBACK_HS_DAC "HS DAC"
#define MIXER_PLAYBACK_HF_DAC "HF DAC"
#define MIXER_MAIN_MIC "Main Mic"
#define MIXER_SUB_MIC "Sub Mic"
#define MIXER_HS_MIC "Headset Mic"
#define MIXER_AMIC0 "AMic0"
#define MIXER_AMIC1 "AMic1"
#define MIXER_BT_LEFT "BT Left"
#define MIXER_BT_RIGHT "BT Right"
#define MIXER_450HZ_HIGH_PASS "450Hz High-pass"
/* ALSA ports for OMAP4 */
#define PORT_MM 0
#define PORT_MM2_UL 1
#define PORT_VX 2
#define PORT_TONES 3
#define PORT_VIBRA 4
#define PORT_MODEM 5
#define PORT_MM_LP 6
#define RESAMPLER_BUFFER_SIZE 8192
#define DEFAULT_OUT_SAMPLING_RATE 44100
/* sampling rate when using MM low power port */
#define MM_LOW_POWER_SAMPLING_RATE 44100
/* sampling rate when using MM full power port */
#define MM_FULL_POWER_SAMPLING_RATE 48000
/* sampling rate when using VX port for narrow band */
#define VX_NB_SAMPLING_RATE 8000
/* conversions from dB to ABE and codec gains */
#define DB_TO_ABE_GAIN(x) ((x) + MIXER_ABE_GAIN_0DB)
#define DB_TO_CAPTURE_PREAMPLIFIER_VOLUME(x) (((x) + 6) / 6)
#define DB_TO_CAPTURE_VOLUME(x) (((x) - 6) / 6)
#define DB_TO_HEADSET_VOLUME(x) (((x) + 30) / 2)
#define DB_TO_SPEAKER_VOLUME(x) (((x) + 52) / 2)
#define DB_TO_EARPIECE_VOLUME(x) (((x) + 24) / 2)
/* use-case specific volumes, all in dB */
#define CAPTURE_MAIN_MIC_VOLUME 13
#define CAPTURE_SUB_MIC_VOLUME 18
#define CAPTURE_HEADSET_MIC_VOLUME 12
#define VOICE_RECOGNITION_MAIN_MIC_VOLUME 13
#define VOICE_RECOGNITION_SUB_MIC_VOLUME 18
#define VOICE_RECOGNITION_HEADSET_MIC_VOLUME 12
#define CAMCORDER_MAIN_MIC_VOLUME 13
#define CAMCORDER_SUB_MIC_VOLUME 18
#define CAMCORDER_HEADSET_MIC_VOLUME 12
#define VOIP_MAIN_MIC_VOLUME 13
#define VOIP_SUB_MIC_VOLUME 20
#define VOIP_HEADSET_MIC_VOLUME 12
#define VOICE_CALL_MAIN_MIC_VOLUME 0
#define VOICE_CALL_SUB_MIC_VOLUME -4
#define VOICE_CALL_HEADSET_MIC_VOLUME 8
/* product-specific defines */
#define PRODUCT_DEVICE_PROPERTY "ro.product.device"
#define PRODUCT_DEVICE_VALUE "toro"
enum tty_modes {
TTY_MODE_OFF,
TTY_MODE_VCO,
TTY_MODE_HCO,
TTY_MODE_FULL
};
struct pcm_config pcm_config_mm = {
.channels = 2,
.rate = MM_FULL_POWER_SAMPLING_RATE,
.period_size = 1024,
.period_count = 4,
.format = PCM_FORMAT_S16_LE,
};
struct pcm_config pcm_config_mm_ul = {
.channels = 2,
.rate = MM_FULL_POWER_SAMPLING_RATE,
.period_size = 1024,
.period_count = 2,
.format = PCM_FORMAT_S16_LE,
};
struct pcm_config pcm_config_vx = {
.channels = 1,
.rate = VX_NB_SAMPLING_RATE,
.period_size = 160,
.period_count = 2,
.format = PCM_FORMAT_S16_LE,
};
#define MIN(x, y) ((x) > (y) ? (y) : (x))
struct route_setting
{
char *ctl_name;
int intval;
char *strval;
};
/* These are values that never change */
struct route_setting defaults[] = {
/* general */
{
.ctl_name = MIXER_DL2_LEFT_EQUALIZER,
.strval = MIXER_450HZ_HIGH_PASS,
},
{
.ctl_name = MIXER_DL2_RIGHT_EQUALIZER,
.strval = MIXER_450HZ_HIGH_PASS,
},
{
.ctl_name = MIXER_DL1_MEDIA_PLAYBACK_VOLUME,
.intval = MIXER_ABE_GAIN_0DB,
},
{
.ctl_name = MIXER_DL2_MEDIA_PLAYBACK_VOLUME,
.intval = MIXER_ABE_GAIN_0DB,
},
{
.ctl_name = MIXER_DL1_VOICE_PLAYBACK_VOLUME,
.intval = MIXER_ABE_GAIN_0DB,
},
{
.ctl_name = MIXER_DL2_VOICE_PLAYBACK_VOLUME,
.intval = MIXER_ABE_GAIN_0DB,
},
{
.ctl_name = MIXER_SDT_DL_VOLUME,
.intval = MIXER_ABE_GAIN_0DB,
},
{
.ctl_name = MIXER_HEADSET_PLAYBACK_VOLUME,
.intval = DB_TO_HEADSET_VOLUME(-6),
},
{
.ctl_name = MIXER_EARPHONE_PLAYBACK_VOLUME,
.intval = DB_TO_EARPIECE_VOLUME(6),
},
{
.ctl_name = MIXER_HANDSFREE_PLAYBACK_VOLUME,
.intval = DB_TO_SPEAKER_VOLUME(0),
},
{
.ctl_name = MIXER_AUDUL_VOICE_UL_VOLUME,
.intval = MIXER_ABE_GAIN_0DB,
},
{
.ctl_name = MIXER_CAPTURE_PREAMPLIFIER_VOLUME,
.intval = DB_TO_CAPTURE_PREAMPLIFIER_VOLUME(0),
},
{
.ctl_name = MIXER_CAPTURE_VOLUME,
.intval = DB_TO_CAPTURE_VOLUME(30),
},
{
.ctl_name = MIXER_SDT_UL_VOLUME,
.intval = MIXER_ABE_GAIN_0DB - 13,
},
{
.ctl_name = MIXER_SIDETONE_MIXER_CAPTURE,
.intval = 0,
},
/* headset */
{
.ctl_name = MIXER_SIDETONE_MIXER_PLAYBACK,
.intval = 1,
},
{
.ctl_name = MIXER_DL1_PDM_SWITCH,
.intval = 1,
},
/* bt */
{
.ctl_name = MIXER_BT_UL_VOLUME,
.intval = MIXER_ABE_GAIN_0DB,
},
{
.ctl_name = NULL,
},
};
struct route_setting hf_output[] = {
{
.ctl_name = MIXER_HF_LEFT_PLAYBACK,
.strval = MIXER_PLAYBACK_HF_DAC,
},
{
.ctl_name = MIXER_HF_RIGHT_PLAYBACK,
.strval = MIXER_PLAYBACK_HF_DAC,
},
{
.ctl_name = NULL,
},
};
struct route_setting hs_output[] = {
{
.ctl_name = MIXER_HS_LEFT_PLAYBACK,
.strval = MIXER_PLAYBACK_HS_DAC,
},
{
.ctl_name = MIXER_HS_RIGHT_PLAYBACK,
.strval = MIXER_PLAYBACK_HS_DAC,
},
{
.ctl_name = NULL,
},
};
/* MM UL front-end paths */
struct route_setting mm_ul2_bt[] = {
{
.ctl_name = MIXER_MUX_UL10,
.strval = MIXER_BT_LEFT,
},
{
.ctl_name = MIXER_MUX_UL11,
.strval = MIXER_BT_RIGHT,
},
{
.ctl_name = NULL,
},
};
struct route_setting mm_ul2_amic[] = {
{
.ctl_name = MIXER_MUX_UL10,
.strval = MIXER_AMIC0,
},
{
.ctl_name = MIXER_MUX_UL11,
.strval = MIXER_AMIC1,
},
{
.ctl_name = NULL,
},
};
/* VX UL front-end paths */
struct route_setting vx_ul_amic_left[] = {
{
.ctl_name = MIXER_MUX_VX0,
.strval = MIXER_AMIC0,
},
{
.ctl_name = MIXER_MUX_VX1,
.strval = MIXER_AMIC1,
},
{
.ctl_name = MIXER_VOICE_CAPTURE_MIXER_CAPTURE,
.intval = 1,
},
{
.ctl_name = NULL,
},
};
struct route_setting vx_ul_amic_right[] = {
{
.ctl_name = MIXER_MUX_VX0,
.strval = MIXER_AMIC1,
},
{
.ctl_name = MIXER_MUX_VX1,
.strval = MIXER_AMIC0,
},
{
.ctl_name = MIXER_VOICE_CAPTURE_MIXER_CAPTURE,
.intval = 1,
},
{
.ctl_name = NULL,
},
};
struct route_setting vx_ul_bt[] = {
{
.ctl_name = MIXER_MUX_VX0,
.strval = MIXER_BT_LEFT,
},
{
.ctl_name = MIXER_MUX_VX1,
.strval = MIXER_BT_RIGHT,
},
{
.ctl_name = MIXER_VOICE_CAPTURE_MIXER_CAPTURE,
.intval = 1,
},
{
.ctl_name = NULL,
},
};
struct mixer_ctls
{
struct mixer_ctl *mm_dl1;
struct mixer_ctl *mm_dl2;
struct mixer_ctl *vx_dl1;
struct mixer_ctl *vx_dl2;
struct mixer_ctl *earpiece_enable;
struct mixer_ctl *dl1_headset;
struct mixer_ctl *dl1_bt;
struct mixer_ctl *left_capture;
struct mixer_ctl *right_capture;
struct mixer_ctl *amic_ul_volume;
struct mixer_ctl *sidetone_capture;
};
struct tuna_audio_device {
struct audio_hw_device hw_device;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
struct mixer *mixer;
struct mixer_ctls mixer_ctls;
int mode;
int devices;
struct pcm *pcm_modem_dl;
struct pcm *pcm_modem_ul;
int in_call;
float voice_volume;
struct tuna_stream_in *active_input;
struct tuna_stream_out *active_output;
bool mic_mute;
int tty_mode;
int sidetone_capture;
struct echo_reference_itfe *echo_reference;
/* RIL */
struct ril_handle ril;
};
struct tuna_stream_out {
struct audio_stream_out stream;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
struct pcm_config config;
struct pcm *pcm;
int device;
struct resampler_itfe *resampler;
char *buffer;
int standby;
struct echo_reference_itfe *echo_reference;
struct tuna_audio_device *dev;
};
#define MAX_PREPROCESSORS 3 /* maximum one AGC + one NS + one AEC per input stream */
struct tuna_stream_in {
struct audio_stream_in stream;
pthread_mutex_t lock; /* see note below on mutex acquisition order */
struct pcm_config config;
struct pcm *pcm;
int device;
struct resampler_itfe *resampler;
struct resampler_buffer_provider buf_provider;
int16_t *buffer;
size_t frames_in;
unsigned int requested_rate;
int port;
int standby;
int source;
struct echo_reference_itfe *echo_reference;
bool need_echo_reference;
effect_handle_t preprocessors[MAX_PREPROCESSORS];
int num_preprocessors;
int16_t *proc_buf;
size_t proc_buf_size;
size_t proc_frames_in;
int16_t *ref_buf;
size_t ref_buf_size;
size_t ref_frames_in;
int read_status;
struct tuna_audio_device *dev;
};
/**
* NOTE: when multiple mutexes have to be acquired, always respect the following order:
* hw device > in stream > out stream
*/
static void select_output_device(struct tuna_audio_device *adev);
static void select_input_device(struct tuna_audio_device *adev);
static int adev_set_voice_volume(struct audio_hw_device *dev, float volume);
static int do_input_standby(struct tuna_stream_in *in);
static int do_output_standby(struct tuna_stream_out *out);
/* Returns true on devices that must use sidetone capture,
* false otherwise. */
static int needs_sidetone_capture(void)
{
char property[PROPERTY_VALUE_MAX];
property_get(PRODUCT_DEVICE_PROPERTY, property, PRODUCT_DEVICE_VALUE);
/* return true if the property matches the given value */
return strcmp(property, PRODUCT_DEVICE_VALUE) == 0;
}
/* The enable flag when 0 makes the assumption that enums are disabled by
* "Off" and integers/booleans by 0 */
static int set_route_by_array(struct mixer *mixer, struct route_setting *route,
int enable)
{
struct mixer_ctl *ctl;
unsigned int i, j;
/* Go through the route array and set each value */
i = 0;
while (route[i].ctl_name) {
ctl = mixer_get_ctl_by_name(mixer, route[i].ctl_name);
if (!ctl)
return -EINVAL;
if (route[i].strval) {
if (enable)
mixer_ctl_set_enum_by_string(ctl, route[i].strval);
else
mixer_ctl_set_enum_by_string(ctl, "Off");
} else {
/* This ensures multiple (i.e. stereo) values are set jointly */
for (j = 0; j < mixer_ctl_get_num_values(ctl); j++) {
if (enable)
mixer_ctl_set_value(ctl, j, route[i].intval);
else
mixer_ctl_set_value(ctl, j, 0);
}
}
i++;
}
return 0;
}
static int start_call(struct tuna_audio_device *adev)
{
/* Open modem PCM channels */
if (adev->pcm_modem_dl == NULL) {
adev->pcm_modem_dl = pcm_open(0, PORT_MODEM, PCM_OUT, &pcm_config_vx);
if (!pcm_is_ready(adev->pcm_modem_dl)) {
LOGE("cannot open PCM modem DL stream: %s", pcm_get_error(adev->pcm_modem_dl));
goto err_open_dl;
}
}
if (adev->pcm_modem_ul == NULL) {
adev->pcm_modem_ul = pcm_open(0, PORT_MODEM, PCM_IN, &pcm_config_vx);
if (!pcm_is_ready(adev->pcm_modem_ul)) {
LOGE("cannot open PCM modem UL stream: %s", pcm_get_error(adev->pcm_modem_ul));
goto err_open_ul;
}
}
pcm_start(adev->pcm_modem_dl);
pcm_start(adev->pcm_modem_ul);
return 0;
err_open_dl:
pcm_close(adev->pcm_modem_dl);
adev->pcm_modem_dl = NULL;
err_open_ul:
pcm_close(adev->pcm_modem_ul);
adev->pcm_modem_ul = NULL;
return -ENOMEM;
}
static void end_call(struct tuna_audio_device *adev)
{
pcm_stop(adev->pcm_modem_dl);
pcm_stop(adev->pcm_modem_ul);
pcm_close(adev->pcm_modem_dl);
pcm_close(adev->pcm_modem_ul);
adev->pcm_modem_dl = NULL;
adev->pcm_modem_ul = NULL;
}
static void set_incall_device(struct tuna_audio_device *adev)
{
int device_type;
switch(adev->devices & AUDIO_DEVICE_OUT_ALL) {
case AUDIO_DEVICE_OUT_EARPIECE:
device_type = SOUND_AUDIO_PATH_HANDSET;
break;
case AUDIO_DEVICE_OUT_SPEAKER:
device_type = SOUND_AUDIO_PATH_SPEAKER;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
device_type = SOUND_AUDIO_PATH_HEADSET;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
device_type = SOUND_AUDIO_PATH_HEADPHONE;
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
device_type = SOUND_AUDIO_PATH_BLUETOOTH;
break;
default:
device_type = SOUND_AUDIO_PATH_HANDSET;
break;
}
/* if output device isn't supported, open modem side to handset by default */
ril_set_call_audio_path(&adev->ril, device_type);
}
static void set_input_volumes(struct tuna_audio_device *adev, int main_mic_on,
int headset_mic_on, int sub_mic_on)
{
unsigned int channel;
int volume = MIXER_ABE_GAIN_0DB;
if (adev->mode == AUDIO_MODE_IN_CALL) {
/* special case: don't look at input source for IN_CALL state */
volume = DB_TO_ABE_GAIN(main_mic_on ? VOICE_CALL_MAIN_MIC_VOLUME :
(headset_mic_on ? VOICE_CALL_HEADSET_MIC_VOLUME :
(sub_mic_on ? VOICE_CALL_SUB_MIC_VOLUME : 0)));
} else if (adev->active_input) {
/* determine input volume by use case */
switch (adev->active_input->source) {
case AUDIO_SOURCE_MIC: /* general capture */
volume = DB_TO_ABE_GAIN(main_mic_on ? CAPTURE_MAIN_MIC_VOLUME :
(headset_mic_on ? CAPTURE_HEADSET_MIC_VOLUME :
(sub_mic_on ? CAPTURE_SUB_MIC_VOLUME : 0)));
break;
case AUDIO_SOURCE_CAMCORDER:
volume = DB_TO_ABE_GAIN(main_mic_on ? CAMCORDER_MAIN_MIC_VOLUME :
(headset_mic_on ? CAMCORDER_HEADSET_MIC_VOLUME :
(sub_mic_on ? CAMCORDER_SUB_MIC_VOLUME : 0)));
break;
case AUDIO_SOURCE_VOICE_RECOGNITION:
volume = DB_TO_ABE_GAIN(main_mic_on ? VOICE_RECOGNITION_MAIN_MIC_VOLUME :
(headset_mic_on ? VOICE_RECOGNITION_HEADSET_MIC_VOLUME :
(sub_mic_on ? VOICE_RECOGNITION_SUB_MIC_VOLUME : 0)));
break;
case AUDIO_SOURCE_VOICE_COMMUNICATION: /* VoIP */
volume = DB_TO_ABE_GAIN(main_mic_on ? VOIP_MAIN_MIC_VOLUME :
(headset_mic_on ? VOIP_HEADSET_MIC_VOLUME :
(sub_mic_on ? VOIP_SUB_MIC_VOLUME : 0)));
break;
default:
/* nothing to do */
break;
}
}
for (channel = 0; channel < 2; channel++)
mixer_ctl_set_value(adev->mixer_ctls.amic_ul_volume, channel, volume);
}
static void force_all_standby(struct tuna_audio_device *adev)
{
struct tuna_stream_in *in;
struct tuna_stream_out *out;
if (adev->active_output) {
out = adev->active_output;
pthread_mutex_lock(&out->lock);
do_output_standby(out);
pthread_mutex_unlock(&out->lock);
}
if (adev->active_input) {
in = adev->active_input;
pthread_mutex_lock(&in->lock);
do_input_standby(in);
pthread_mutex_unlock(&in->lock);
}
}
static void select_mode(struct tuna_audio_device *adev)
{
if (adev->mode == AUDIO_MODE_IN_CALL) {
if (!adev->in_call) {
force_all_standby(adev);
select_output_device(adev);
start_call(adev);
ril_set_call_clock_sync(&adev->ril, SOUND_CLOCK_START);
adev_set_voice_volume(&adev->hw_device, adev->voice_volume);
adev->in_call = 1;
}
} else {
if (adev->in_call) {
adev->in_call = 0;
end_call(adev);
force_all_standby(adev);
select_output_device(adev);
select_input_device(adev);
}
}
}
static void select_output_device(struct tuna_audio_device *adev)
{
int headset_on;
int headphone_on;
int speaker_on;
int earpiece_on;
int bt_on;
int dl1_on;
int sidetone_capture_on = 0;
/* tear down call stream before changing route,
otherwise microphone does not function */
if (adev->in_call)
end_call(adev);
headset_on = adev->devices & AUDIO_DEVICE_OUT_WIRED_HEADSET;
headphone_on = adev->devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
speaker_on = adev->devices & AUDIO_DEVICE_OUT_SPEAKER;
earpiece_on = adev->devices & AUDIO_DEVICE_OUT_EARPIECE;
bt_on = adev->devices & AUDIO_DEVICE_OUT_ALL_SCO;
/* force rx path according to TTY mode when in call */
if (adev->mode == AUDIO_MODE_IN_CALL && !bt_on) {
switch(adev->tty_mode) {
case TTY_MODE_FULL:
case TTY_MODE_VCO:
/* rx path to headphones */
headphone_on = 1;
headset_on = 0;
speaker_on = 0;
earpiece_on = 0;
break;
case TTY_MODE_HCO:
/* rx path to device speaker */
headphone_on = 0;
headset_on = 0;
speaker_on = 1;
earpiece_on = 0;
break;
case TTY_MODE_OFF:
default:
break;
}
}
dl1_on = headset_on | headphone_on | earpiece_on | bt_on;
/* Select front end */
mixer_ctl_set_value(adev->mixer_ctls.mm_dl2, 0, speaker_on);
mixer_ctl_set_value(adev->mixer_ctls.vx_dl2, 0,
speaker_on && (adev->mode == AUDIO_MODE_IN_CALL));
mixer_ctl_set_value(adev->mixer_ctls.mm_dl1, 0, dl1_on);
mixer_ctl_set_value(adev->mixer_ctls.vx_dl1, 0,
dl1_on && (adev->mode == AUDIO_MODE_IN_CALL));
/* Select back end */
mixer_ctl_set_value(adev->mixer_ctls.dl1_headset, 0,
headset_on | headphone_on | earpiece_on);
mixer_ctl_set_value(adev->mixer_ctls.dl1_bt, 0, bt_on);
mixer_ctl_set_value(adev->mixer_ctls.earpiece_enable, 0, earpiece_on);
/* select output stage */
set_route_by_array(adev->mixer, hs_output, headset_on | headphone_on | earpiece_on);
set_route_by_array(adev->mixer, hf_output, speaker_on);
/* Special case: select input path if in a call, otherwise
in_set_parameters is used to update the input route
todo: use sub mic for handsfree case */
if (adev->mode == AUDIO_MODE_IN_CALL) {
if (bt_on)
set_route_by_array(adev->mixer, vx_ul_bt, bt_on);
else {
/* force tx path according to TTY mode when in call */
switch(adev->tty_mode) {
case TTY_MODE_FULL:
case TTY_MODE_HCO:
/* tx path from headset mic */
headphone_on = 0;
headset_on = 1;
speaker_on = 0;
earpiece_on = 0;
break;
case TTY_MODE_VCO:
/* tx path from device sub mic */
headphone_on = 0;
headset_on = 0;
speaker_on = 1;
earpiece_on = 0;
break;
case TTY_MODE_OFF:
default:
break;
}
if (headset_on || headphone_on || earpiece_on)
set_route_by_array(adev->mixer, vx_ul_amic_left, 1);
else if (speaker_on)
set_route_by_array(adev->mixer, vx_ul_amic_right, 1);
else
set_route_by_array(adev->mixer, vx_ul_amic_left, 0);
mixer_ctl_set_enum_by_string(adev->mixer_ctls.left_capture,
(earpiece_on || headphone_on) ? MIXER_MAIN_MIC :
(headset_on ? MIXER_HS_MIC : "Off"));
mixer_ctl_set_enum_by_string(adev->mixer_ctls.right_capture,
speaker_on ? MIXER_SUB_MIC : "Off");
set_input_volumes(adev, earpiece_on || headphone_on,
headset_on, speaker_on);
/* enable sidetone mixer capture if needed */
sidetone_capture_on = earpiece_on && adev->sidetone_capture;
}
set_incall_device(adev);
}
mixer_ctl_set_value(adev->mixer_ctls.sidetone_capture, 0, sidetone_capture_on);
if (adev->in_call)
start_call(adev);
}
static void select_input_device(struct tuna_audio_device *adev)
{
int headset_on = 0;
int main_mic_on = 0;
int sub_mic_on = 0;
int bt_on = adev->devices & AUDIO_DEVICE_IN_ALL_SCO;
int port = PORT_VX;
int anlg_mic_on;
if (!bt_on) {
if ((adev->mode != AUDIO_MODE_IN_CALL) && (adev->active_input != 0)) {
/* PORT_MM2_UL is only used when not in call and active input uses it. */
port = adev->active_input->port;
/* sub mic is used for camcorder or VoIP on speaker phone */
sub_mic_on = (adev->active_input->source == AUDIO_SOURCE_CAMCORDER) ||
((adev->devices & AUDIO_DEVICE_OUT_SPEAKER) &&
(adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION));
}
if (!sub_mic_on) {
headset_on = adev->devices & AUDIO_DEVICE_IN_WIRED_HEADSET;
main_mic_on = adev->devices & AUDIO_DEVICE_IN_BUILTIN_MIC;
}
}
anlg_mic_on = headset_on | main_mic_on | sub_mic_on;
/* tear down call stream before changing route,
* otherwise microphone does not function
*/
if (adev->in_call)
end_call(adev);
/* TODO: check how capture is possible during voice calls or if
* both use cases are mutually exclusive.
*/
if (bt_on) {
set_route_by_array(adev->mixer, mm_ul2_bt, (port != PORT_VX));
set_route_by_array(adev->mixer, vx_ul_bt, (port == PORT_VX));
} else {
/* Select front end */
set_route_by_array(adev->mixer, mm_ul2_amic,
anlg_mic_on && (port != PORT_VX));
set_route_by_array(adev->mixer, vx_ul_amic_left,
anlg_mic_on && (port == PORT_VX));
/* Select back end */
mixer_ctl_set_enum_by_string(adev->mixer_ctls.right_capture,
sub_mic_on ? MIXER_SUB_MIC : "Off");
mixer_ctl_set_enum_by_string(adev->mixer_ctls.left_capture,
main_mic_on ? MIXER_MAIN_MIC :
(headset_on ? MIXER_HS_MIC : "Off"));
}
set_input_volumes(adev, main_mic_on, headset_on, sub_mic_on);
if (adev->in_call)
start_call(adev);
}
/* must be called with hw device and output stream mutexes locked */
static int start_output_stream(struct tuna_stream_out *out)
{
struct tuna_audio_device *adev = out->dev;
adev->active_output = out;
if (adev->mode != AUDIO_MODE_IN_CALL) {
/* FIXME: only works if only one output can be active at a time */
adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
adev->devices |= out->device;
select_output_device(adev);
}
out->pcm = pcm_open(0, PORT_MM, PCM_OUT, &out->config);
if (!pcm_is_ready(out->pcm)) {
LOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm));
pcm_close(out->pcm);
adev->active_output = NULL;
return -ENOMEM;
}
if (adev->echo_reference != NULL)
out->echo_reference = adev->echo_reference;
out->resampler->reset(out->resampler);
return 0;
}
static int check_input_parameters(uint32_t sample_rate, int format, int channel_count)
{
if (format != AUDIO_FORMAT_PCM_16_BIT)
return -EINVAL;
if ((channel_count < 1) || (channel_count > 2))
return -EINVAL;
switch(sample_rate) {
case 8000:
case 11025:
case 16000:
case 22050:
case 24000:
case 32000:
case 44100:
case 48000:
break;
default:
return -EINVAL;
}
return 0;
}
static size_t get_input_buffer_size(uint32_t sample_rate, int format, int channel_count)
{
size_t size;
size_t device_rate;
if (check_input_parameters(sample_rate, format, channel_count) != 0)
return 0;
/* 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_mm_ul.period_size * sample_rate) / pcm_config_mm_ul.rate;
size = ((size + 15) / 16) * 16;
return size * channel_count * sizeof(short);
}
static void add_echo_reference(struct tuna_stream_out *out,
struct echo_reference_itfe *reference)
{
pthread_mutex_lock(&out->lock);
out->echo_reference = reference;
pthread_mutex_unlock(&out->lock);
}
static void remove_echo_reference(struct tuna_stream_out *out,
struct echo_reference_itfe *reference)
{
pthread_mutex_lock(&out->lock);
if (out->echo_reference == reference) {
/* stop writing to echo reference */
reference->write(reference, NULL);
out->echo_reference = NULL;
}
pthread_mutex_unlock(&out->lock);
}
static void put_echo_reference(struct tuna_audio_device *adev,
struct echo_reference_itfe *reference)
{
if (adev->echo_reference != NULL &&
reference == adev->echo_reference) {
if (adev->active_output != NULL)
remove_echo_reference(adev->active_output, reference);
release_echo_reference(reference);
adev->echo_reference = NULL;
}
}
static struct echo_reference_itfe *get_echo_reference(struct tuna_audio_device *adev,
audio_format_t format,
uint32_t channel_count,
uint32_t sampling_rate)
{
put_echo_reference(adev, adev->echo_reference);
if (adev->active_output != NULL) {
struct audio_stream *stream = &adev->active_output->stream.common;
uint32_t wr_channel_count = popcount(stream->get_channels(stream));
uint32_t wr_sampling_rate = stream->get_sample_rate(stream);
int status = create_echo_reference(AUDIO_FORMAT_PCM_16_BIT,
channel_count,
sampling_rate,
AUDIO_FORMAT_PCM_16_BIT,
wr_channel_count,
wr_sampling_rate,
&adev->echo_reference);
if (status == 0)
add_echo_reference(adev->active_output, adev->echo_reference);
}
return adev->echo_reference;
}
static int get_playback_delay(struct tuna_stream_out *out,
size_t frames,
struct echo_reference_buffer *buffer)
{
size_t kernel_frames;
int status;
status = pcm_get_htimestamp(out->pcm, &kernel_frames, &buffer->time_stamp);
if (status < 0) {
buffer->time_stamp.tv_sec = 0;
buffer->time_stamp.tv_nsec = 0;
buffer->delay_ns = 0;
LOGV("get_playback_delay(): pcm_get_htimestamp error,"
"setting playbackTimestamp to 0");
return status;
}
kernel_frames = pcm_get_buffer_size(out->pcm) - kernel_frames;
/* adjust render time stamp with delay added by current driver buffer.
* Add the duration of current frame as we want the render time of the last
* sample being written. */
buffer->delay_ns = (long)(((int64_t)(kernel_frames + frames)* 1000000000)/
MM_FULL_POWER_SAMPLING_RATE);
return 0;
}
static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
return DEFAULT_OUT_SAMPLING_RATE;
}
static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
return 0;
}
static size_t out_get_buffer_size(const struct audio_stream *stream)
{
struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
/* 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_t size = (out->config.period_size * DEFAULT_OUT_SAMPLING_RATE) /
out->config.rate;
size = ((size + 15) / 16) * 16;
return size * audio_stream_frame_size((struct audio_stream *)stream);
}
static uint32_t out_get_channels(const struct audio_stream *stream)
{
return AUDIO_CHANNEL_OUT_STEREO;
}
static int out_get_format(const struct audio_stream *stream)
{
return AUDIO_FORMAT_PCM_16_BIT;
}
static int out_set_format(struct audio_stream *stream, int format)
{
return 0;
}
/* must be called with hw device and output stream mutexes locked */
static int do_output_standby(struct tuna_stream_out *out)
{
struct tuna_audio_device *adev = out->dev;
if (!out->standby) {
pcm_close(out->pcm);
out->pcm = NULL;
adev->active_output = 0;
/* if in call, don't turn off the output stage. This will
be done when the call is ended */
if (adev->mode != AUDIO_MODE_IN_CALL) {
/* FIXME: only works if only one output can be active at a time */
adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
set_route_by_array(adev->mixer, hs_output, 0);
set_route_by_array(adev->mixer, hf_output, 0);
}
/* stop writing to echo reference */
if (out->echo_reference != NULL) {
out->echo_reference->write(out->echo_reference, NULL);
out->echo_reference = NULL;
}
out->standby = 1;
}
return 0;
}
static int out_standby(struct audio_stream *stream)
{
struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
int status;
pthread_mutex_lock(&out->dev->lock);
pthread_mutex_lock(&out->lock);
status = do_output_standby(out);
pthread_mutex_unlock(&out->lock);
pthread_mutex_unlock(&out->dev->lock);
return status;
}
static int out_dump(const struct audio_stream *stream, int fd)
{
return 0;
}
static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
struct tuna_audio_device *adev = out->dev;
struct tuna_stream_in *in;
struct str_parms *parms;
char *str;
char value[32];
int ret, val = 0;
bool force_input_standby = false;
parms = str_parms_create_str(kvpairs);
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
if (ret >= 0) {
val = atoi(value);
pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&out->lock);
if ((out->device != val) && (val != 0)) {
out->device = val;
if (adev->mode == AUDIO_MODE_IN_CALL) {
adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
adev->devices |= out->device;
select_output_device(adev);
} else if (out == adev->active_output) {
do_output_standby(out);
/* a change in output device may change the microphone selection */
if (adev->active_input &&
adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION) {
force_input_standby = true;
}
}
}
pthread_mutex_unlock(&out->lock);
if (force_input_standby) {
in = adev->active_input;
pthread_mutex_lock(&in->lock);
do_input_standby(in);
pthread_mutex_unlock(&in->lock);
}
pthread_mutex_unlock(&adev->lock);
}
str_parms_destroy(parms);
return ret;
}
static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
{
return strdup("");
}
static uint32_t out_get_latency(const struct audio_stream_out *stream)
{
struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
return (out->config.period_size * out->config.period_count * 1000) /
out->config.rate;
}
static int out_set_volume(struct audio_stream_out *stream, float left,
float right)
{
return -ENOSYS;
}
static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
size_t bytes)
{
int ret;
struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
struct tuna_audio_device *adev = out->dev;
size_t frame_size = audio_stream_frame_size(&out->stream.common);
size_t in_frames = bytes / frame_size;
size_t out_frames = RESAMPLER_BUFFER_SIZE / frame_size;
unsigned int total_bytes;
unsigned int max_bytes;
unsigned int remaining_bytes;
unsigned int pos;
bool force_input_standby = false;
struct tuna_stream_in *in;
/* acquiring hw device mutex systematically is useful if a low priority thread is waiting
* on the output stream mutex - e.g. executing select_mode() while holding the hw device
* mutex
*/
pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&out->lock);
if (out->standby) {
ret = start_output_stream(out);
if (ret == 0) {
out->standby = 0;
/* a change in output device may change the microphone selection */
if (adev->active_input &&
adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION)
force_input_standby = true;
}
}
pthread_mutex_unlock(&adev->lock);
out->resampler->resample_from_input(out->resampler,
(int16_t *)buffer,
&in_frames,
(int16_t *)out->buffer,
&out_frames);
total_bytes = out_frames * frame_size;
max_bytes = out->config.period_size * frame_size;
remaining_bytes = total_bytes;
if (out->echo_reference != NULL) {
struct echo_reference_buffer b;
b.raw = (void *)buffer;
b.frame_count = out_frames;
get_playback_delay(out, out_frames, &b);
out->echo_reference->write(out->echo_reference, &b);
}
for (pos = 0; pos < total_bytes; pos += max_bytes) {
int bytes_to_write = MIN(max_bytes, remaining_bytes);
ret = pcm_write(out->pcm, (void *)(out->buffer + pos), bytes_to_write);
if (ret != 0) {
usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
out_get_sample_rate(&stream->common));
pthread_mutex_unlock(&out->lock);
return bytes;
}
remaining_bytes -= bytes_to_write;
}
pthread_mutex_unlock(&out->lock);
if (force_input_standby) {
pthread_mutex_lock(&adev->lock);
if (adev->active_input) {
in = adev->active_input;
pthread_mutex_lock(&in->lock);
do_input_standby(in);
pthread_mutex_unlock(&in->lock);
}
pthread_mutex_unlock(&adev->lock);
}
return bytes;
}
static int out_get_render_position(const struct audio_stream_out *stream,
uint32_t *dsp_frames)
{
return -EINVAL;
}
static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
{
return 0;
}
static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
{
return 0;
}
/** audio_stream_in implementation **/
/* must be called with hw device and input stream mutexes locked */
static int start_input_stream(struct tuna_stream_in *in)
{
int ret = 0;
struct tuna_audio_device *adev = in->dev;
adev->active_input = in;
if (adev->mode != AUDIO_MODE_IN_CALL) {
adev->devices &= ~AUDIO_DEVICE_IN_ALL;
adev->devices |= in->device;
select_input_device(adev);
}
if (in->need_echo_reference && in->echo_reference == NULL)
in->echo_reference = get_echo_reference(adev,
AUDIO_FORMAT_PCM_16_BIT,
in->config.channels,
in->requested_rate);
/* this assumes routing is done previously */
in->pcm = pcm_open(0, in->port, PCM_IN, &in->config);
if (!pcm_is_ready(in->pcm)) {
LOGE("cannot open pcm_in driver: %s", pcm_get_error(in->pcm));
pcm_close(in->pcm);
adev->active_input = NULL;
return -ENOMEM;
}
/* if no supported sample rate is available, use the resampler */
if (in->resampler) {
in->resampler->reset(in->resampler);
in->frames_in = 0;
}
return 0;
}
static uint32_t in_get_sample_rate(const struct audio_stream *stream)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
return in->requested_rate;
}
static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
return 0;
}
static size_t in_get_buffer_size(const struct audio_stream *stream)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
return get_input_buffer_size(in->requested_rate,
AUDIO_FORMAT_PCM_16_BIT,
in->config.channels);
}
static uint32_t in_get_channels(const struct audio_stream *stream)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
if (in->config.channels == 1) {
return AUDIO_CHANNEL_IN_MONO;
} else {
return AUDIO_CHANNEL_IN_STEREO;
}
}
static int in_get_format(const struct audio_stream *stream)
{
return AUDIO_FORMAT_PCM_16_BIT;
}
static int in_set_format(struct audio_stream *stream, int format)
{
return 0;
}
/* must be called with hw device and input stream mutexes locked */
static int do_input_standby(struct tuna_stream_in *in)
{
struct tuna_audio_device *adev = in->dev;
if (!in->standby) {
pcm_close(in->pcm);
in->pcm = NULL;
adev->active_input = 0;
if (adev->mode != AUDIO_MODE_IN_CALL) {
adev->devices &= ~AUDIO_DEVICE_IN_ALL;
select_input_device(adev);
}
if (in->echo_reference != NULL) {
/* stop reading from echo reference */
in->echo_reference->read(in->echo_reference, NULL);
put_echo_reference(adev, in->echo_reference);
in->echo_reference = NULL;
}
in->standby = 1;
}
return 0;
}
static int in_standby(struct audio_stream *stream)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
int status;
pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
status = do_input_standby(in);
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
return status;
}
static int in_dump(const struct audio_stream *stream, int fd)
{
return 0;
}
static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
struct tuna_audio_device *adev = in->dev;
struct str_parms *parms;
char *str;
char value[32];
int ret, val = 0;
bool do_standby = false;
parms = str_parms_create_str(kvpairs);
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, sizeof(value));
pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&in->lock);
if (ret >= 0) {
val = atoi(value);
/* no audio source uses val == 0 */
if ((in->source != val) && (val != 0)) {
in->source = val;
do_standby = true;
}
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
if (ret >= 0) {
val = atoi(value);
if ((in->device != val) && (val != 0)) {
in->device = val;
do_standby = true;
}
}
if (do_standby)
do_input_standby(in);
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&adev->lock);
str_parms_destroy(parms);
return ret;
}
static char * in_get_parameters(const struct audio_stream *stream,
const char *keys)
{
return strdup("");
}
static int in_set_gain(struct audio_stream_in *stream, float gain)
{
return 0;
}
static void get_capture_delay(struct tuna_stream_in *in,
size_t frames,
struct echo_reference_buffer *buffer)
{
/* read frames available in kernel driver buffer */
size_t kernel_frames;
struct timespec tstamp;
long buf_delay;
long rsmp_delay;
long kernel_delay;
long delay_ns;
if (pcm_get_htimestamp(in->pcm, &kernel_frames, &tstamp) < 0) {
buffer->time_stamp.tv_sec = 0;
buffer->time_stamp.tv_nsec = 0;
buffer->delay_ns = 0;
LOGW("read get_capture_delay(): pcm_htimestamp error");
return;
}
/* read frames available in audio HAL input buffer
* add number of frames being read as we want the capture time of first sample
* in current buffer */
buf_delay = (long)(((int64_t)(in->frames_in + in->proc_frames_in) * 1000000000)
/ in->config.rate);
/* add delay introduced by resampler */
rsmp_delay = 0;
if (in->resampler) {
rsmp_delay = in->resampler->delay_ns(in->resampler);
}
kernel_delay = (long)(((int64_t)kernel_frames * 1000000000) / in->config.rate);
delay_ns = kernel_delay + buf_delay + rsmp_delay;
buffer->time_stamp = tstamp;
buffer->delay_ns = delay_ns;
LOGV("get_capture_delay time_stamp = [%ld].[%ld], delay_ns: [%d],"
" kernel_delay:[%ld], buf_delay:[%ld], rsmp_delay:[%ld], kernel_frames:[%d], "
"in->frames_in:[%d], in->proc_frames_in:[%d], frames:[%d]",
buffer->time_stamp.tv_sec , buffer->time_stamp.tv_nsec, buffer->delay_ns,
kernel_delay, buf_delay, rsmp_delay, kernel_frames,
in->frames_in, in->proc_frames_in, frames);
}
static int32_t update_echo_reference(struct tuna_stream_in *in, size_t frames)
{
struct echo_reference_buffer b;
b.delay_ns = 0;
LOGV("update_echo_reference, frames = [%d], in->ref_frames_in = [%d], "
"b.frame_count = [%d]",
frames, in->ref_frames_in, frames - in->ref_frames_in);
if (in->ref_frames_in < frames) {
if (in->ref_buf_size < frames) {
in->ref_buf_size = frames;
in->ref_buf = (int16_t *)realloc(in->ref_buf,
in->ref_buf_size *
in->config.channels * sizeof(int16_t));
}
b.frame_count = frames - in->ref_frames_in;
b.raw = (void *)(in->ref_buf + in->ref_frames_in * in->config.channels);
get_capture_delay(in, frames, &b);
if (in->echo_reference->read(in->echo_reference, &b) == 0)
{
in->ref_frames_in += b.frame_count;
LOGV("update_echo_reference: in->ref_frames_in:[%d], "
"in->ref_buf_size:[%d], frames:[%d], b.frame_count:[%d]",
in->ref_frames_in, in->ref_buf_size, frames, b.frame_count);
}
} else
LOGW("update_echo_reference: NOT enough frames to read ref buffer");
return b.delay_ns;
}
static int set_preprocessor_param(effect_handle_t handle,
effect_param_t *param)
{
uint32_t size = sizeof(int);
uint32_t psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) +
param->vsize;
int status = (*handle)->command(handle,
EFFECT_CMD_SET_PARAM,
sizeof (effect_param_t) + psize,
param,
&size,
&param->status);
if (status == 0)
status = param->status;
return status;
}
static int set_preprocessor_echo_delay(effect_handle_t handle,
int32_t delay_us)
{
uint32_t buf[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
effect_param_t *param = (effect_param_t *)buf;
param->psize = sizeof(uint32_t);
param->vsize = sizeof(uint32_t);
*(uint32_t *)param->data = AEC_PARAM_ECHO_DELAY;
*((int32_t *)param->data + 1) = delay_us;
return set_preprocessor_param(handle, param);
}
static void push_echo_reference(struct tuna_stream_in *in, size_t frames)
{
/* read frames from echo reference buffer and update echo delay
* in->ref_frames_in is updated with frames available in in->ref_buf */
int32_t delay_us = update_echo_reference(in, frames)/1000;
int i;
audio_buffer_t buf;
if (in->ref_frames_in < frames)
frames = in->ref_frames_in;
buf.frameCount = frames;
buf.raw = in->ref_buf;
for (i = 0; i < in->num_preprocessors; i++) {
if ((*in->preprocessors[i])->process_reverse == NULL)
continue;
(*in->preprocessors[i])->process_reverse(in->preprocessors[i],
&buf,
NULL);
set_preprocessor_echo_delay(in->preprocessors[i], delay_us);
}
in->ref_frames_in -= buf.frameCount;
if (in->ref_frames_in) {
memcpy(in->ref_buf,
in->ref_buf + buf.frameCount * in->config.channels,
in->ref_frames_in * in->config.channels * sizeof(int16_t));
}
}
static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
struct resampler_buffer* buffer)
{
struct tuna_stream_in *in;
if (buffer_provider == NULL || buffer == NULL)
return -EINVAL;
in = (struct tuna_stream_in *)((char *)buffer_provider -
offsetof(struct tuna_stream_in, buf_provider));
if (in->pcm == NULL) {
buffer->raw = NULL;
buffer->frame_count = 0;
in->read_status = -ENODEV;
return -ENODEV;
}
if (in->frames_in == 0) {
in->read_status = pcm_read(in->pcm,
(void*)in->buffer,
in->config.period_size *
audio_stream_frame_size(&in->stream.common));
if (in->read_status != 0) {
LOGE("get_next_buffer() pcm_read error %d", in->read_status);
buffer->raw = NULL;
buffer->frame_count = 0;
return in->read_status;
}
in->frames_in = in->config.period_size;
}
buffer->frame_count = (buffer->frame_count > in->frames_in) ?
in->frames_in : buffer->frame_count;
buffer->i16 = in->buffer + (in->config.period_size - in->frames_in) *
in->config.channels;
return in->read_status;
}
static void release_buffer(struct resampler_buffer_provider *buffer_provider,
struct resampler_buffer* buffer)
{
struct tuna_stream_in *in;
if (buffer_provider == NULL || buffer == NULL)
return;
in = (struct tuna_stream_in *)((char *)buffer_provider -
offsetof(struct tuna_stream_in, buf_provider));
in->frames_in -= buffer->frame_count;
}
/* read_frames() reads frames from kernel driver, down samples to capture rate
* if necessary and output the number of frames requested to the buffer specified */
static ssize_t read_frames(struct tuna_stream_in *in, void *buffer, ssize_t frames)
{
ssize_t frames_wr = 0;
while (frames_wr < frames) {
size_t frames_rd = frames - frames_wr;
if (in->resampler != NULL) {
in->resampler->resample_from_provider(in->resampler,
(int16_t *)((char *)buffer +
frames_wr * audio_stream_frame_size(&in->stream.common)),
&frames_rd);
} else {
struct resampler_buffer buf = {
{ raw : NULL, },
frame_count : frames_rd,
};
get_next_buffer(&in->buf_provider, &buf);
if (buf.raw != NULL) {
memcpy((char *)buffer +
frames_wr * audio_stream_frame_size(&in->stream.common),
buf.raw,
buf.frame_count * audio_stream_frame_size(&in->stream.common));
frames_rd = buf.frame_count;
}
release_buffer(&in->buf_provider, &buf);
}
/* in->read_status is updated by getNextBuffer() also called by
* in->resampler->resample_from_provider() */
if (in->read_status != 0)
return in->read_status;
frames_wr += frames_rd;
}
return frames_wr;
}
/* process_frames() reads frames from kernel driver (via read_frames()),
* calls the active audio pre processings and output the number of frames requested
* to the buffer specified */
static ssize_t process_frames(struct tuna_stream_in *in, void* buffer, ssize_t frames)
{
ssize_t frames_wr = 0;
audio_buffer_t in_buf;
audio_buffer_t out_buf;
int i;
while (frames_wr < frames) {
/* first reload enough frames at the end of process input buffer */
if (in->proc_frames_in < (size_t)frames) {
ssize_t frames_rd;
if (in->proc_buf_size < (size_t)frames) {
in->proc_buf_size = (size_t)frames;
in->proc_buf = (int16_t *)realloc(in->proc_buf,
in->proc_buf_size *
in->config.channels * sizeof(int16_t));
LOGV("process_frames(): in->proc_buf %p size extended to %d frames",
in->proc_buf, in->proc_buf_size);
}
frames_rd = read_frames(in,
in->proc_buf +
in->proc_frames_in * in->config.channels,
frames - in->proc_frames_in);
if (frames_rd < 0) {
frames_wr = frames_rd;
break;
}
in->proc_frames_in += frames_rd;
}
if (in->echo_reference != NULL)
push_echo_reference(in, in->proc_frames_in);
/* in_buf.frameCount and out_buf.frameCount indicate respectively
* the maximum number of frames to be consumed and produced by process() */
in_buf.frameCount = in->proc_frames_in;
in_buf.s16 = in->proc_buf;
out_buf.frameCount = frames - frames_wr;
out_buf.s16 = (int16_t *)buffer + frames_wr * in->config.channels;
for (i = 0; i < in->num_preprocessors; i++)
(*in->preprocessors[i])->process(in->preprocessors[i],
&in_buf,
&out_buf);
/* process() has updated the number of frames consumed and produced in
* in_buf.frameCount and out_buf.frameCount respectively
* move remaining frames to the beginning of in->proc_buf */
in->proc_frames_in -= in_buf.frameCount;
if (in->proc_frames_in) {
memcpy(in->proc_buf,
in->proc_buf + in_buf.frameCount * in->config.channels,
in->proc_frames_in * in->config.channels * sizeof(int16_t));
}
/* if not enough frames were passed to process(), read more and retry. */
if (out_buf.frameCount == 0)
continue;
frames_wr += out_buf.frameCount;
}
return frames_wr;
}
static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
size_t bytes)
{
int ret = 0;
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
struct tuna_audio_device *adev = in->dev;
size_t frames_rq = bytes / audio_stream_frame_size(&stream->common);
/* acquiring hw device mutex systematically is useful if a low priority thread is waiting
* on the input stream mutex - e.g. executing select_mode() while holding the hw device
* mutex
*/
pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&in->lock);
if (in->standby) {
ret = start_input_stream(in);
if (ret == 0)
in->standby = 0;
}
pthread_mutex_unlock(&adev->lock);
if (ret < 0)
goto exit;
if (in->num_preprocessors != 0)
ret = process_frames(in, buffer, frames_rq);
else if (in->resampler != NULL)
ret = read_frames(in, buffer, frames_rq);
else
ret = pcm_read(in->pcm, buffer, bytes);
if (ret > 0)
ret = 0;
if (ret == 0 && adev->mic_mute)
memset(buffer, 0, bytes);
exit:
if (ret < 0)
usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
in_get_sample_rate(&stream->common));
pthread_mutex_unlock(&in->lock);
return bytes;
}
static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
{
return 0;
}
static int in_add_audio_effect(const struct audio_stream *stream,
effect_handle_t effect)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
int status;
effect_descriptor_t desc;
pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
if (in->num_preprocessors >= MAX_PREPROCESSORS) {
status = -ENOSYS;
goto exit;
}
status = (*effect)->get_descriptor(effect, &desc);
if (status != 0)
goto exit;
in->preprocessors[in->num_preprocessors++] = effect;
if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
in->need_echo_reference = true;
do_input_standby(in);
}
exit:
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
return status;
}
static int in_remove_audio_effect(const struct audio_stream *stream,
effect_handle_t effect)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
int i;
int status = -EINVAL;
bool found = false;
effect_descriptor_t desc;
pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
if (in->num_preprocessors <= 0) {
status = -ENOSYS;
goto exit;
}
for (i = 0; i < in->num_preprocessors; i++) {
if (found) {
in->preprocessors[i - 1] = in->preprocessors[i];
continue;
}
if (in->preprocessors[i] == effect) {
in->preprocessors[i] = NULL;
status = 0;
found = true;
}
}
if (status != 0)
goto exit;
in->num_preprocessors--;
status = (*effect)->get_descriptor(effect, &desc);
if (status != 0)
goto exit;
if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
in->need_echo_reference = false;
do_input_standby(in);
}
exit:
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
return status;
}
static int adev_open_output_stream(struct audio_hw_device *dev,
uint32_t devices, int *format,
uint32_t *channels, uint32_t *sample_rate,
struct audio_stream_out **stream_out)
{
struct tuna_audio_device *ladev = (struct tuna_audio_device *)dev;
struct tuna_stream_out *out;
int ret;
out = (struct tuna_stream_out *)calloc(1, sizeof(struct tuna_stream_out));
if (!out)
return -ENOMEM;
ret = create_resampler(DEFAULT_OUT_SAMPLING_RATE,
MM_FULL_POWER_SAMPLING_RATE,
2,
RESAMPLER_QUALITY_DEFAULT,
NULL,
&out->resampler);
if (ret != 0)
goto err_open;
out->buffer = malloc(RESAMPLER_BUFFER_SIZE); /* todo: allow for reallocing */
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->config = pcm_config_mm;
out->device = devices;
out->dev = ladev;
out->standby = 1;
*format = out_get_format(&out->stream.common);
*channels = out_get_channels(&out->stream.common);
*sample_rate = out_get_sample_rate(&out->stream.common);
*stream_out = &out->stream;
return 0;
err_open:
free(out);
*stream_out = NULL;
return ret;
}
static void adev_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream)
{
struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
out_standby(&stream->common);
if (out->buffer)
free(out->buffer);
if (out->resampler)
release_resampler(out->resampler);
free(stream);
}
static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
{
struct tuna_audio_device *adev = (struct tuna_audio_device *)dev;
struct str_parms *parms;
char *str;
char value[32];
int ret;
parms = str_parms_create_str(kvpairs);
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value));
if (ret >= 0) {
int tty_mode;
if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0)
tty_mode = TTY_MODE_OFF;
else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0)
tty_mode = TTY_MODE_VCO;
else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0)
tty_mode = TTY_MODE_HCO;
else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0)
tty_mode = TTY_MODE_FULL;
else
return -EINVAL;
pthread_mutex_lock(&adev->lock);
if (tty_mode != adev->tty_mode) {
adev->tty_mode = tty_mode;
if (adev->mode == AUDIO_MODE_IN_CALL)
select_output_device(adev);
}
pthread_mutex_unlock(&adev->lock);
}
str_parms_destroy(parms);
return ret;
}
static char * adev_get_parameters(const struct audio_hw_device *dev,
const char *keys)
{
return strdup("");
}
static int adev_init_check(const struct audio_hw_device *dev)
{
return 0;
}
static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
{
struct tuna_audio_device *adev = (struct tuna_audio_device *)dev;
adev->voice_volume = volume;
if (adev->mode == AUDIO_MODE_IN_CALL)
ril_set_call_volume(&adev->ril, SOUND_TYPE_VOICE, volume);
return 0;
}
static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
{
return -ENOSYS;
}
static int adev_set_mode(struct audio_hw_device *dev, int mode)
{
struct tuna_audio_device *adev = (struct tuna_audio_device *)dev;
pthread_mutex_lock(&adev->lock);
if (adev->mode != mode) {
adev->mode = mode;
select_mode(adev);
}
pthread_mutex_unlock(&adev->lock);
return 0;
}
static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
{
struct tuna_audio_device *adev = (struct tuna_audio_device *)dev;
adev->mic_mute = state;
return 0;
}
static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
{
struct tuna_audio_device *adev = (struct tuna_audio_device *)dev;
*state = adev->mic_mute;
return 0;
}
static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
uint32_t sample_rate, int format,
int channel_count)
{
size_t size;
if (check_input_parameters(sample_rate, format, channel_count) != 0)
return 0;
return get_input_buffer_size(sample_rate, format, channel_count);
}
static int adev_open_input_stream(struct audio_hw_device *dev, uint32_t devices,
int *format, uint32_t *channel_mask,
uint32_t *sample_rate,
audio_in_acoustics_t acoustics,
struct audio_stream_in **stream_in)
{
struct tuna_audio_device *ladev = (struct tuna_audio_device *)dev;
struct tuna_stream_in *in;
int ret;
int channel_count = popcount(*channel_mask);
if (check_input_parameters(*sample_rate, *format, channel_count) != 0)
return -EINVAL;
in = (struct tuna_stream_in *)calloc(1, sizeof(struct tuna_stream_in));
if (!in)
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->requested_rate = *sample_rate;
in->port = PORT_MM2_UL; /* use multimedia uplink 2 */
memcpy(&in->config, &pcm_config_mm_ul, sizeof(pcm_config_mm_ul));
in->config.channels = channel_count;
in->buffer = malloc(in->config.period_size *
audio_stream_frame_size(&in->stream.common));
if (!in->buffer) {
ret = -ENOMEM;
goto err;
}
if (in->requested_rate != in->config.rate) {
in->buf_provider.get_next_buffer = get_next_buffer;
in->buf_provider.release_buffer = release_buffer;
ret = create_resampler(in->config.rate,
in->requested_rate,
in->config.channels,
RESAMPLER_QUALITY_DEFAULT,
&in->buf_provider,
&in->resampler);
if (ret != 0) {
ret = -EINVAL;
goto err;
}
}
in->dev = ladev;
in->standby = 1;
in->device = devices;
*stream_in = &in->stream;
return 0;
err:
if (in->resampler)
release_resampler(in->resampler);
free(in);
*stream_in = NULL;
return ret;
}
static void adev_close_input_stream(struct audio_hw_device *dev,
struct audio_stream_in *stream)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
in_standby(&stream->common);
if (in->resampler) {
free(in->buffer);
release_resampler(in->resampler);
}
free(stream);
return;
}
static int adev_dump(const audio_hw_device_t *device, int fd)
{
return 0;
}
static int adev_close(hw_device_t *device)
{
struct tuna_audio_device *adev = (struct tuna_audio_device *)device;
/* RIL */
ril_close(&adev->ril);
mixer_close(adev->mixer);
free(device);
return 0;
}
static uint32_t adev_get_supported_devices(const struct audio_hw_device *dev)
{
return (/* OUT */
AUDIO_DEVICE_OUT_EARPIECE |
AUDIO_DEVICE_OUT_SPEAKER |
AUDIO_DEVICE_OUT_WIRED_HEADSET |
AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
AUDIO_DEVICE_OUT_AUX_DIGITAL |
AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET |
AUDIO_DEVICE_OUT_ALL_SCO |
AUDIO_DEVICE_OUT_DEFAULT |
/* IN */
AUDIO_DEVICE_IN_COMMUNICATION |
AUDIO_DEVICE_IN_AMBIENT |
AUDIO_DEVICE_IN_BUILTIN_MIC |
AUDIO_DEVICE_IN_WIRED_HEADSET |
AUDIO_DEVICE_IN_AUX_DIGITAL |
AUDIO_DEVICE_IN_BACK_MIC |
AUDIO_DEVICE_IN_ALL_SCO |
AUDIO_DEVICE_IN_DEFAULT);
}
static int adev_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
struct tuna_audio_device *adev;
int ret;
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
return -EINVAL;
adev = calloc(1, sizeof(struct tuna_audio_device));
if (!adev)
return -ENOMEM;
adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
adev->hw_device.common.version = 0;
adev->hw_device.common.module = (struct hw_module_t *) module;
adev->hw_device.common.close = adev_close;
adev->hw_device.get_supported_devices = adev_get_supported_devices;
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.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->mixer = mixer_open(0);
if (!adev->mixer) {
free(adev);
LOGE("Unable to open the mixer, aborting.");
return -EINVAL;
}
adev->mixer_ctls.mm_dl1 = mixer_get_ctl_by_name(adev->mixer,
MIXER_DL1_MIXER_MULTIMEDIA);
adev->mixer_ctls.vx_dl1 = mixer_get_ctl_by_name(adev->mixer,
MIXER_DL1_MIXER_VOICE);
adev->mixer_ctls.mm_dl2 = mixer_get_ctl_by_name(adev->mixer,
MIXER_DL2_MIXER_MULTIMEDIA);
adev->mixer_ctls.vx_dl2 = mixer_get_ctl_by_name(adev->mixer,
MIXER_DL2_MIXER_VOICE);
adev->mixer_ctls.dl1_headset = mixer_get_ctl_by_name(adev->mixer,
MIXER_DL1_PDM_SWITCH);
adev->mixer_ctls.dl1_bt = mixer_get_ctl_by_name(adev->mixer,
MIXER_DL1_BT_VX_SWITCH);
adev->mixer_ctls.earpiece_enable = mixer_get_ctl_by_name(adev->mixer,
MIXER_EARPHONE_ENABLE_SWITCH);
adev->mixer_ctls.left_capture = mixer_get_ctl_by_name(adev->mixer,
MIXER_ANALOG_LEFT_CAPTURE_ROUTE);
adev->mixer_ctls.right_capture = mixer_get_ctl_by_name(adev->mixer,
MIXER_ANALOG_RIGHT_CAPTURE_ROUTE);
adev->mixer_ctls.amic_ul_volume = mixer_get_ctl_by_name(adev->mixer,
MIXER_AMIC_UL_VOLUME);
adev->mixer_ctls.sidetone_capture = mixer_get_ctl_by_name(adev->mixer,
MIXER_SIDETONE_MIXER_CAPTURE);
if (!adev->mixer_ctls.mm_dl1 || !adev->mixer_ctls.vx_dl1 ||
!adev->mixer_ctls.mm_dl2 || !adev->mixer_ctls.vx_dl2 ||
!adev->mixer_ctls.dl1_headset || !adev->mixer_ctls.dl1_bt ||
!adev->mixer_ctls.earpiece_enable || !adev->mixer_ctls.left_capture ||
!adev->mixer_ctls.right_capture || !adev->mixer_ctls.amic_ul_volume ||
!adev->mixer_ctls.sidetone_capture) {
mixer_close(adev->mixer);
free(adev);
LOGE("Unable to locate all mixer controls, aborting.");
return -EINVAL;
}
/* Set the default route before the PCM stream is opened */
pthread_mutex_lock(&adev->lock);
set_route_by_array(adev->mixer, defaults, 1);
adev->mode = AUDIO_MODE_NORMAL;
adev->devices = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_IN_BUILTIN_MIC;
select_output_device(adev);
adev->pcm_modem_dl = NULL;
adev->pcm_modem_ul = NULL;
adev->voice_volume = 1.0f;
adev->tty_mode = TTY_MODE_OFF;
adev->sidetone_capture = needs_sidetone_capture();
/* RIL */
ril_open(&adev->ril);
pthread_mutex_unlock(&adev->lock);
*device = &adev->hw_device.common;
return 0;
}
static struct hw_module_methods_t hal_module_methods = {
.open = adev_open,
};
struct audio_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = AUDIO_HARDWARE_MODULE_ID,
.name = "Tuna audio HW HAL",
.author = "The Android Open Source Project",
.methods = &hal_module_methods,
},
};