/* * 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" /*#define LOG_NDEBUG 0*/ #include <errno.h> #include <pthread.h> #include <stdint.h> #include <stdlib.h> #include <sys/time.h> #include <fcntl.h> #include <cutils/log.h> #include <cutils/properties.h> #include <cutils/str_parms.h> #include <hardware/audio.h> #include <hardware/hardware.h> #include <linux/videodev2.h> #include <videodev2_exynos_media.h> #include <system/audio.h> #include <tinyalsa/asoundlib.h> #include <audio_utils/resampler.h> #include <audio_route/audio_route.h> #include <BubbleLevel.h> #include <eS305VoiceProcessing.h> #define PCM_CARD 0 #define PCM_CARD_SPDIF 1 #define PCM_TOTAL 2 #define PCM_DEVICE 0 #define PCM_DEVICE_DEEP 1 #define PCM_DEVICE_VOICE 2 #define PCM_DEVICE_SCO 3 #define MIXER_CARD 0 /* duration in ms of volume ramp applied when starting capture to remove plop */ #define CAPTURE_START_RAMP_MS 100 /* default sampling for HDMI multichannel output */ #define HDMI_MULTI_DEFAULT_SAMPLING_RATE 44100 /* maximum number of channel mask configurations supported. Currently the primary * output only supports 1 (stereo) and the multi channel HDMI output 2 (5.1 and 7.1) */ #define MAX_SUPPORTED_CHANNEL_MASKS 2 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) struct pcm_config pcm_config = { .channels = 2, .rate = 44100, .period_size = 256, .period_count = 2, .format = PCM_FORMAT_S16_LE, }; struct pcm_config pcm_config_in = { .channels = 2, .rate = 44100, .period_size = 1024, .period_count = 2, .format = PCM_FORMAT_S16_LE, }; struct pcm_config pcm_config_sco = { .channels = 1, .rate = 8000, .period_size = 128, .period_count = 2, .format = PCM_FORMAT_S16_LE, }; struct pcm_config pcm_config_deep = { .channels = 2, .rate = 44100, /* FIXME This is an arbitrary number, may change. * Dynamic configuration based on screen on/off is not implemented; * let's see what power consumption is first to see if necessary. */ .period_size = 8192, .period_count = 2, .format = PCM_FORMAT_S16_LE, }; struct pcm_config pcm_config_hdmi_multi = { .channels = 6, /* changed when the stream is opened */ .rate = HDMI_MULTI_DEFAULT_SAMPLING_RATE, .period_size = 1024, .period_count = 4, .format = PCM_FORMAT_S16_LE, }; enum output_type { OUTPUT_DEEP_BUF, // deep PCM buffers output stream OUTPUT_LOW_LATENCY, // low latency output stream OUTPUT_HDMI, // HDMI multi channel OUTPUT_TOTAL }; struct audio_device { struct audio_hw_device hw_device; pthread_mutex_t lock; /* see note below on mutex acquisition order */ audio_devices_t out_device; /* "or" of stream_out.device for all active output streams */ audio_devices_t in_device; bool mic_mute; struct audio_route *ar; audio_source_t input_source; int cur_route_id; /* current route ID: combination of input source * and output device IDs */ struct pcm *pcm_voice_out; struct pcm *pcm_sco_out; struct pcm *pcm_voice_in; struct pcm *pcm_sco_in; int es305_preset; int es305_new_mode; int es305_mode; int hdmi_drv_fd; struct bubble_level *bubble_level; audio_channel_mask_t in_channel_mask; unsigned int sco_on_count; struct stream_out *outputs[OUTPUT_TOTAL]; }; struct stream_out { struct audio_stream_out stream; pthread_mutex_t lock; /* see note below on mutex acquisition order */ struct pcm *pcm[PCM_TOTAL]; struct pcm_config config; unsigned int pcm_device; bool standby; /* true if all PCMs are inactive */ audio_devices_t device; /* FIXME: when HDMI multichannel output is active, other outputs must be disabled as * HDMI and WM1811 share the same I2S. This means that notifications and other sounds are * silent when watching a 5.1 movie. */ bool disabled; audio_channel_mask_t channel_mask; /* Array of supported channel mask configurations. +1 so that the last entry is always 0 */ audio_channel_mask_t supported_channel_masks[MAX_SUPPORTED_CHANNEL_MASKS + 1]; bool muted; uint64_t written; /* total frames written, not cleared when entering standby */ struct audio_device *dev; }; struct stream_in { struct audio_stream_in stream; pthread_mutex_t lock; /* see note below on mutex acquisition order */ struct pcm *pcm; bool standby; unsigned int requested_rate; struct resampler_itfe *resampler; struct resampler_buffer_provider buf_provider; int16_t *buffer; size_t frames_in; int read_status; audio_source_t input_source; audio_io_handle_t io_handle; audio_devices_t device; uint16_t ramp_vol; uint16_t ramp_step; size_t ramp_frames; audio_channel_mask_t channel_mask; struct audio_device *dev; }; #define STRING_TO_ENUM(string) { #string, string } struct string_to_enum { const char *name; uint32_t value; }; const struct string_to_enum out_channels_name_to_enum_table[] = { STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1), }; enum { OUT_DEVICE_SPEAKER, OUT_DEVICE_HEADSET, OUT_DEVICE_HEADPHONES, OUT_DEVICE_BT_SCO, OUT_DEVICE_SPEAKER_AND_HEADSET, OUT_DEVICE_TAB_SIZE, /* number of rows in route_configs[][] */ OUT_DEVICE_NONE, OUT_DEVICE_CNT }; enum { IN_SOURCE_MIC, IN_SOURCE_CAMCORDER, IN_SOURCE_VOICE_RECOGNITION, IN_SOURCE_VOICE_COMMUNICATION, IN_SOURCE_TAB_SIZE, /* number of lines in route_configs[][] */ IN_SOURCE_NONE, IN_SOURCE_CNT }; enum { ES305_MODE_DEFAULT, ES305_MODE_LEVEL, ES305_NUM_MODES, }; int get_output_device_id(audio_devices_t device) { if (device == AUDIO_DEVICE_NONE) return OUT_DEVICE_NONE; if (popcount(device) == 2) { if ((device == (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET)) || (device == (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE))) return OUT_DEVICE_SPEAKER_AND_HEADSET; else return OUT_DEVICE_NONE; } if (popcount(device) != 1) return OUT_DEVICE_NONE; switch (device) { case AUDIO_DEVICE_OUT_SPEAKER: return OUT_DEVICE_SPEAKER; case AUDIO_DEVICE_OUT_WIRED_HEADSET: return OUT_DEVICE_HEADSET; case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: return OUT_DEVICE_HEADPHONES; case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: return OUT_DEVICE_BT_SCO; default: return OUT_DEVICE_NONE; } } int get_input_source_id(audio_source_t source) { switch (source) { case AUDIO_SOURCE_DEFAULT: return IN_SOURCE_NONE; case AUDIO_SOURCE_MIC: return IN_SOURCE_MIC; case AUDIO_SOURCE_CAMCORDER: return IN_SOURCE_CAMCORDER; case AUDIO_SOURCE_VOICE_RECOGNITION: return IN_SOURCE_VOICE_RECOGNITION; case AUDIO_SOURCE_VOICE_COMMUNICATION: return IN_SOURCE_VOICE_COMMUNICATION; default: return IN_SOURCE_NONE; } } struct route_config { const char * const output_route; const char * const input_route; int es305_preset[ES305_NUM_MODES]; // es305 preset for this route. // -1 means es305 bypass }; const struct route_config media_speaker = { "media-speaker", "media-main-mic", { ES305_PRESET_OFF, ES305_PRESET_OFF } }; const struct route_config media_headphones = { "media-headphones", "media-main-mic", { ES305_PRESET_OFF, ES305_PRESET_OFF } }; const struct route_config media_headset = { "media-headphones", "media-headset-mic", { ES305_PRESET_OFF, ES305_PRESET_OFF } }; const struct route_config camcorder_speaker = { "media-speaker", "media-second-mic", { ES305_PRESET_CAMCORDER, ES305_PRESET_CAMCORDER } }; const struct route_config camcorder_headphones = { "media-headphones", "media-second-mic", { ES305_PRESET_CAMCORDER, ES305_PRESET_CAMCORDER } }; const struct route_config voice_rec_speaker = { "voice-rec-speaker", "voice-rec-main-mic", { ES305_PRESET_ASRA_HANDHELD, ES305_PRESET_ASRA_DESKTOP } }; const struct route_config voice_rec_headphones = { "voice-rec-headphones", "voice-rec-main-mic", { ES305_PRESET_ASRA_HANDHELD, ES305_PRESET_ASRA_DESKTOP } }; const struct route_config voice_rec_headset = { "voice-rec-headphones", "voice-rec-headset-mic", { ES305_PRESET_ASRA_HEADSET, ES305_PRESET_ASRA_HEADSET } }; const struct route_config communication_speaker = { "communication-speaker", "communication-main-mic", { ES305_PRESET_VOIP_HANDHELD, ES305_PRESET_VOIP_DESKTOP } }; const struct route_config communication_headphones = { "communication-headphones", "communication-main-mic", { ES305_PRESET_VOIP_HEADPHONES, ES305_PRESET_VOIP_HP_DESKTOP} }; const struct route_config communication_headset = { "communication-headphones", "communication-headset-mic", { ES305_PRESET_VOIP_HEADSET, ES305_PRESET_VOIP_HEADSET } }; const struct route_config speaker_and_headphones = { "speaker-and-headphones", "main-mic", { ES305_PRESET_CURRENT, ES305_PRESET_CURRENT } }; const struct route_config bluetooth_sco = { "bt-sco-headset", "bt-sco-mic", { ES305_PRESET_OFF, ES305_PRESET_OFF } }; const struct route_config * const route_configs[IN_SOURCE_TAB_SIZE] [OUT_DEVICE_TAB_SIZE] = { { /* IN_SOURCE_MIC */ &media_speaker, /* OUT_DEVICE_SPEAKER */ &media_headset, /* OUT_DEVICE_HEADSET */ &media_headphones, /* OUT_DEVICE_HEADPHONES */ &bluetooth_sco, /* OUT_DEVICE_BT_SCO */ &speaker_and_headphones /* OUT_DEVICE_SPEAKER_AND_HEADSET */ }, { /* IN_SOURCE_CAMCORDER */ &camcorder_speaker, /* OUT_DEVICE_SPEAKER */ &camcorder_headphones, /* OUT_DEVICE_HEADSET */ &camcorder_headphones, /* OUT_DEVICE_HEADPHONES */ &bluetooth_sco, /* OUT_DEVICE_BT_SCO */ &speaker_and_headphones /* OUT_DEVICE_SPEAKER_AND_HEADSET */ }, { /* IN_SOURCE_VOICE_RECOGNITION */ &voice_rec_speaker, /* OUT_DEVICE_SPEAKER */ &voice_rec_headset, /* OUT_DEVICE_HEADSET */ &voice_rec_headphones, /* OUT_DEVICE_HEADPHONES */ &bluetooth_sco, /* OUT_DEVICE_BT_SCO */ &speaker_and_headphones /* OUT_DEVICE_SPEAKER_AND_HEADSET */ }, { /* IN_SOURCE_VOICE_COMMUNICATION */ &communication_speaker, /* OUT_DEVICE_SPEAKER */ &communication_headset, /* OUT_DEVICE_HEADSET */ &communication_headphones, /* OUT_DEVICE_HEADPHONES */ &bluetooth_sco, /* OUT_DEVICE_BT_SCO */ &speaker_and_headphones /* OUT_DEVICE_SPEAKER_AND_HEADSET */ } }; static int do_out_standby(struct stream_out *out); /** * NOTE: when multiple mutexes have to be acquired, always respect the * following order: hw device > in stream > out stream */ /* Helper functions */ static int open_hdmi_driver(struct audio_device *adev) { if (adev->hdmi_drv_fd < 0) { adev->hdmi_drv_fd = open("/dev/video16", O_RDWR); if (adev->hdmi_drv_fd < 0) ALOGE("%s cannot open video16 (%d)", __func__, adev->hdmi_drv_fd); } return adev->hdmi_drv_fd; } /* must be called with hw device mutex locked */ static int enable_hdmi_audio(struct audio_device *adev, int enable) { int ret; struct v4l2_control ctrl; ret = open_hdmi_driver(adev); if (ret < 0) return ret; ctrl.id = V4L2_CID_TV_ENABLE_HDMI_AUDIO; ctrl.value = !!enable; ret = ioctl(adev->hdmi_drv_fd, VIDIOC_S_CTRL, &ctrl); if (ret < 0) ALOGE("V4L2_CID_TV_ENABLE_HDMI_AUDIO ioctl error (%d)", errno); return ret; } /* must be called with hw device mutex locked */ static int read_hdmi_channel_masks(struct audio_device *adev, struct stream_out *out) { int ret; struct v4l2_control ctrl; ret = open_hdmi_driver(adev); if (ret < 0) return ret; ctrl.id = V4L2_CID_TV_MAX_AUDIO_CHANNELS; ret = ioctl(adev->hdmi_drv_fd, VIDIOC_G_CTRL, &ctrl); if (ret < 0) { ALOGE("V4L2_CID_TV_MAX_AUDIO_CHANNELS ioctl error (%d)", errno); return ret; } ALOGV("%s ioctl %d got %d max channels", __func__, ret, ctrl.value); if (ctrl.value != 6 && ctrl.value != 8) return -ENOSYS; out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1; if (ctrl.value == 8) out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_7POINT1; return ret; } /* must be called with hw device mutex locked */ static int set_hdmi_channels(struct audio_device *adev, int channels) { int ret; struct v4l2_control ctrl; ret = open_hdmi_driver(adev); if (ret < 0) return ret; ctrl.id = V4L2_CID_TV_SET_NUM_CHANNELS; ctrl.value = channels; ret = ioctl(adev->hdmi_drv_fd, VIDIOC_S_CTRL, &ctrl); if (ret < 0) ALOGE("V4L2_CID_TV_SET_NUM_CHANNELS ioctl error (%d)", errno); return ret; } static void select_devices(struct audio_device *adev) { int output_device_id = get_output_device_id(adev->out_device); int input_source_id = get_input_source_id(adev->input_source); const char *output_route = NULL; const char *input_route = NULL; int new_route_id; int new_es305_preset = -1; audio_route_reset(adev->ar); enable_hdmi_audio(adev, adev->out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL); new_route_id = (1 << (input_source_id + OUT_DEVICE_CNT)) + (1 << output_device_id); if ((new_route_id == adev->cur_route_id) && (adev->es305_mode == adev->es305_new_mode)) return; adev->cur_route_id = new_route_id; adev->es305_mode = adev->es305_new_mode; if (input_source_id != IN_SOURCE_NONE) { if (output_device_id != OUT_DEVICE_NONE) { input_route = route_configs[input_source_id][output_device_id]->input_route; output_route = route_configs[input_source_id][output_device_id]->output_route; new_es305_preset = route_configs[input_source_id][output_device_id]->es305_preset[adev->es305_mode]; } else { switch (adev->in_device) { case AUDIO_DEVICE_IN_WIRED_HEADSET & ~AUDIO_DEVICE_BIT_IN: output_device_id = OUT_DEVICE_HEADSET; break; case AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET & ~AUDIO_DEVICE_BIT_IN: output_device_id = OUT_DEVICE_BT_SCO; break; default: output_device_id = OUT_DEVICE_SPEAKER; break; } input_route = route_configs[input_source_id][output_device_id]->input_route; new_es305_preset = route_configs[input_source_id][output_device_id]->es305_preset[adev->es305_mode]; } // disable noise suppression when capturing front and back mic for voice recognition if ((adev->input_source == AUDIO_SOURCE_VOICE_RECOGNITION) && (adev->in_channel_mask == AUDIO_CHANNEL_IN_FRONT_BACK)) new_es305_preset = -1; } else { if (output_device_id != OUT_DEVICE_NONE) { output_route = route_configs[IN_SOURCE_MIC][output_device_id]->output_route; } } ALOGV("select_devices() devices %#x input src %d output route %s input route %s", adev->out_device, adev->input_source, output_route ? output_route : "none", input_route ? input_route : "none"); if (output_route) audio_route_apply_path(adev->ar, output_route); if (input_route) audio_route_apply_path(adev->ar, input_route); if ((new_es305_preset != ES305_PRESET_CURRENT) && (new_es305_preset != adev->es305_preset)) { ALOGV(" select_devices() changing es305 preset from %d to %d", adev->es305_preset, new_es305_preset); if (eS305_UsePreset(new_es305_preset) == 0) { adev->es305_preset = new_es305_preset; } } audio_route_update_mixer(adev->ar); } void bubblelevel_callback(bool is_level, void *user_data) { struct audio_device *adev = (struct audio_device *)user_data; int es305_mode; if (is_level) es305_mode = ES305_MODE_LEVEL; else es305_mode = ES305_MODE_DEFAULT; pthread_mutex_lock(&adev->lock); if (es305_mode != adev->es305_mode) { adev->es305_new_mode = es305_mode; select_devices(adev); ALOGV("bubblelevel_callback is_level %d es305_mode %d", is_level, es305_mode); } pthread_mutex_unlock(&adev->lock); } /* must be called with hw device mutex locked */ bool get_bubblelevel(struct audio_device *adev) { if (!adev->bubble_level) { adev->bubble_level = bubble_level_create(); if (adev->bubble_level) adev->bubble_level->set_callback(adev->bubble_level, bubblelevel_callback, adev); } return (adev->bubble_level != NULL); } static void force_non_hdmi_out_standby(struct audio_device *adev) { enum output_type type; struct stream_out *out; for (type = 0; type < OUTPUT_TOTAL; ++type) { out = adev->outputs[type]; if (type == OUTPUT_HDMI || !out) continue; pthread_mutex_lock(&out->lock); do_out_standby(out); pthread_mutex_unlock(&out->lock); } } /* must be called with the hw device mutex locked, OK to hold other mutexes */ static void start_bt_sco(struct audio_device *adev) { if (adev->sco_on_count++ > 0) return; adev->pcm_voice_out = pcm_open(PCM_CARD, PCM_DEVICE_VOICE, PCM_OUT | PCM_MONOTONIC, &pcm_config_sco); if (adev->pcm_voice_out && !pcm_is_ready(adev->pcm_voice_out)) { ALOGE("pcm_open(VOICE_OUT) failed: %s", pcm_get_error(adev->pcm_voice_out)); goto err_voice_out; } adev->pcm_sco_out = pcm_open(PCM_CARD, PCM_DEVICE_SCO, PCM_OUT | PCM_MONOTONIC, &pcm_config_sco); if (adev->pcm_sco_out && !pcm_is_ready(adev->pcm_sco_out)) { ALOGE("pcm_open(SCO_OUT) failed: %s", pcm_get_error(adev->pcm_sco_out)); goto err_sco_out; } adev->pcm_voice_in = pcm_open(PCM_CARD, PCM_DEVICE_VOICE, PCM_IN, &pcm_config_sco); if (adev->pcm_voice_in && !pcm_is_ready(adev->pcm_voice_in)) { ALOGE("pcm_open(VOICE_IN) failed: %s", pcm_get_error(adev->pcm_voice_in)); goto err_voice_in; } adev->pcm_sco_in = pcm_open(PCM_CARD, PCM_DEVICE_SCO, PCM_IN, &pcm_config_sco); if (adev->pcm_sco_in && !pcm_is_ready(adev->pcm_sco_in)) { ALOGE("pcm_open(SCO_IN) failed: %s", pcm_get_error(adev->pcm_sco_in)); goto err_sco_in; } pcm_start(adev->pcm_voice_out); pcm_start(adev->pcm_sco_out); pcm_start(adev->pcm_voice_in); pcm_start(adev->pcm_sco_in); return; err_sco_in: pcm_close(adev->pcm_sco_in); err_voice_in: pcm_close(adev->pcm_voice_in); err_sco_out: pcm_close(adev->pcm_sco_out); err_voice_out: pcm_close(adev->pcm_voice_out); } /* must be called with the hw device mutex locked, OK to hold other mutexes */ static void stop_bt_sco(struct audio_device *adev) { if (adev->sco_on_count == 0 || --adev->sco_on_count > 0) return; pcm_stop(adev->pcm_voice_out); pcm_stop(adev->pcm_sco_out); pcm_stop(adev->pcm_voice_in); pcm_stop(adev->pcm_sco_in); pcm_close(adev->pcm_voice_out); pcm_close(adev->pcm_sco_out); pcm_close(adev->pcm_voice_in); pcm_close(adev->pcm_sco_in); } /* 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; int type; if (out == adev->outputs[OUTPUT_HDMI]) { force_non_hdmi_out_standby(adev); } else if (adev->outputs[OUTPUT_HDMI] && !adev->outputs[OUTPUT_HDMI]->standby) { out->disabled = true; return 0; } out->disabled = false; if (out->device & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | AUDIO_DEVICE_OUT_AUX_DIGITAL | AUDIO_DEVICE_OUT_ALL_SCO)) { out->pcm[PCM_CARD] = pcm_open(PCM_CARD, out->pcm_device, PCM_OUT | PCM_MONOTONIC, &out->config); if (out->pcm[PCM_CARD] && !pcm_is_ready(out->pcm[PCM_CARD])) { ALOGE("pcm_open(PCM_CARD) failed: %s", pcm_get_error(out->pcm[PCM_CARD])); pcm_close(out->pcm[PCM_CARD]); return -ENOMEM; } } if (out->device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { out->pcm[PCM_CARD_SPDIF] = pcm_open(PCM_CARD_SPDIF, out->pcm_device, PCM_OUT | PCM_MONOTONIC, &out->config); if (out->pcm[PCM_CARD_SPDIF] && !pcm_is_ready(out->pcm[PCM_CARD_SPDIF])) { ALOGE("pcm_open(PCM_CARD_SPDIF) failed: %s", pcm_get_error(out->pcm[PCM_CARD_SPDIF])); pcm_close(out->pcm[PCM_CARD_SPDIF]); return -ENOMEM; } } adev->out_device |= out->device; select_devices(adev); if (out->device & AUDIO_DEVICE_OUT_ALL_SCO) start_bt_sco(adev); if (out->device & AUDIO_DEVICE_OUT_AUX_DIGITAL) set_hdmi_channels(adev, out->config.channels); /* anticipate level measurement in case we start capture later */ if (get_bubblelevel(adev)) adev->bubble_level->poll_once(adev->bubble_level); 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; in->pcm = pcm_open(PCM_CARD, PCM_DEVICE, PCM_IN, &pcm_config_in); if (in->pcm && !pcm_is_ready(in->pcm)) { ALOGE("pcm_open() failed: %s", pcm_get_error(in->pcm)); pcm_close(in->pcm); return -ENOMEM; } /* if no supported sample rate is available, use the resampler */ if (in->resampler) in->resampler->reset(in->resampler); in->frames_in = 0; adev->input_source = in->input_source; adev->in_device = in->device; adev->in_channel_mask = in->channel_mask; eS305_SetActiveIoHandle(in->io_handle); select_devices(adev); if (in->device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) start_bt_sco(adev); /* initialize volume ramp */ in->ramp_frames = (CAPTURE_START_RAMP_MS * in->requested_rate) / 1000; in->ramp_step = (uint16_t)(USHRT_MAX / in->ramp_frames); in->ramp_vol = 0;; if (get_bubblelevel(adev)) { adev->bubble_level->set_poll_interval(adev->bubble_level, BL_POLL_INTERVAL_MIN_SEC); adev->bubble_level->start_polling(adev->bubble_level); } return 0; } static size_t get_input_buffer_size(unsigned int sample_rate, audio_format_t format, unsigned int channel_count) { 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 * sample_rate) / pcm_config_in.rate; size = ((size + 15) / 16) * 16; return size * channel_count * audio_bytes_per_sample(format); } static int get_next_buffer(struct resampler_buffer_provider *buffer_provider, struct resampler_buffer* buffer) { struct stream_in *in; size_t i; if (buffer_provider == NULL || buffer == NULL) return -EINVAL; in = (struct stream_in *)((char *)buffer_provider - offsetof(struct 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, pcm_frames_to_bytes(in->pcm, pcm_config_in.period_size)); if (in->read_status != 0) { ALOGE("get_next_buffer() pcm_read error %d", in->read_status); buffer->raw = NULL; buffer->frame_count = 0; return in->read_status; } in->frames_in = pcm_config_in.period_size; /* Do stereo to mono conversion in place by discarding right channel */ if (in->channel_mask == AUDIO_CHANNEL_IN_MONO) for (i = 1; i < in->frames_in; i++) in->buffer[i] = in->buffer[i * 2]; } buffer->frame_count = (buffer->frame_count > in->frames_in) ? in->frames_in : buffer->frame_count; buffer->i16 = in->buffer + (pcm_config_in.period_size - in->frames_in) * popcount(in->channel_mask); return in->read_status; } static void release_buffer(struct resampler_buffer_provider *buffer_provider, struct resampler_buffer* buffer) { struct stream_in *in; if (buffer_provider == NULL || buffer == NULL) return; in = (struct stream_in *)((char *)buffer_provider - offsetof(struct 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 stream_in *in, void *buffer, ssize_t frames) { ssize_t frames_wr = 0; size_t frame_size = audio_stream_frame_size(&in->stream.common); 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 * frame_size), &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 * frame_size, buf.raw, buf.frame_count * frame_size); 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; } /* API functions */ static uint32_t out_get_sample_rate(const struct audio_stream *stream) { struct stream_out *out = (struct stream_out *)stream; return out->config.rate; } static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) { return -ENOSYS; } static size_t out_get_buffer_size(const struct audio_stream *stream) { struct stream_out *out = (struct stream_out *)stream; return out->config.period_size * audio_stream_frame_size((struct audio_stream *)stream); } static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) { struct stream_out *out = (struct stream_out *)stream; return out->channel_mask; } static audio_format_t out_get_format(const struct audio_stream *stream) { return AUDIO_FORMAT_PCM_16_BIT; } static int out_set_format(struct audio_stream *stream, audio_format_t format) { return -ENOSYS; } /* Return the set of output devices associated with active streams * other than out. Assumes out is non-NULL and out->dev is locked. */ static audio_devices_t output_devices(struct stream_out *out) { struct audio_device *dev = out->dev; enum output_type type; audio_devices_t devices = AUDIO_DEVICE_NONE; for (type = 0; type < OUTPUT_TOTAL; ++type) { struct stream_out *other = dev->outputs[type]; if (other && (other != out) && !other->standby) { /* safe to access other stream without a mutex, * because we hold the dev lock, * which prevents the other stream from being closed */ devices |= other->device; } } return devices; } static int do_out_standby(struct stream_out *out) { struct audio_device *adev = out->dev; int i; if (!out->standby) { for (i = 0; i < PCM_TOTAL; i++) { if (out->pcm[i]) { pcm_close(out->pcm[i]); out->pcm[i] = NULL; } } out->standby = true; if (out == adev->outputs[OUTPUT_HDMI]) { /* force standby on low latency output stream so that it can reuse HDMI driver if * necessary when restarted */ force_non_hdmi_out_standby(adev); } if (out->device & AUDIO_DEVICE_OUT_ALL_SCO) stop_bt_sco(adev); /* re-calculate the set of active devices from other streams */ adev->out_device = output_devices(out); /* Skip resetting the mixer if no output device is active */ if (adev->out_device) select_devices(adev); } return 0; } static int out_standby(struct audio_stream *stream) { struct stream_out *out = (struct stream_out *)stream; int ret; pthread_mutex_lock(&out->dev->lock); pthread_mutex_lock(&out->lock); ret = do_out_standby(out); pthread_mutex_unlock(&out->lock); pthread_mutex_unlock(&out->dev->lock); return ret; } 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 stream_out *out = (struct stream_out *)stream; struct audio_device *adev = out->dev; struct str_parms *parms; char value[32]; int ret; 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); pthread_mutex_lock(&out->lock); if (ret >= 0) { val = atoi(value); if ((out->device != val) && (val != 0)) { /* Force standby if moving to/from SPDIF or if the output * device changes when in SPDIF mode */ if (((val & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) ^ (adev->out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET)) || (adev->out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET)) { do_out_standby(out); } /* force output standby to start or stop SCO pcm stream if needed */ if ((val & AUDIO_DEVICE_OUT_ALL_SCO) ^ (out->device & AUDIO_DEVICE_OUT_ALL_SCO)) { do_out_standby(out); } if (!out->standby && (out == adev->outputs[OUTPUT_HDMI] || !adev->outputs[OUTPUT_HDMI] || adev->outputs[OUTPUT_HDMI]->standby)) { adev->out_device = output_devices(out) | val; select_devices(adev); } out->device = val; } } pthread_mutex_unlock(&out->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) { struct stream_out *out = (struct stream_out *)stream; struct str_parms *query = str_parms_create_str(keys); char *str; char value[256]; struct str_parms *reply = str_parms_create(); size_t i, j; int ret; bool first = true; ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value, sizeof(value)); if (ret >= 0) { value[0] = '\0'; i = 0; /* the last entry in supported_channel_masks[] is always 0 */ while (out->supported_channel_masks[i] != 0) { for (j = 0; j < ARRAY_SIZE(out_channels_name_to_enum_table); j++) { if (out_channels_name_to_enum_table[j].value == out->supported_channel_masks[i]) { if (!first) { strcat(value, "|"); } strcat(value, out_channels_name_to_enum_table[j].name); first = false; break; } } i++; } str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value); str = str_parms_to_str(reply); } else { str = strdup(keys); } str_parms_destroy(query); str_parms_destroy(reply); return str; } static uint32_t out_get_latency(const struct audio_stream_out *stream) { struct stream_out *out = (struct 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) { struct stream_out *out = (struct stream_out *)stream; struct audio_device *adev = out->dev; if (out == adev->outputs[OUTPUT_HDMI]) { /* only take left channel into account: the API is for stereo anyway */ out->muted = (left == 0.0f); return 0; } return -ENOSYS; } 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; int i; /* * 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 (out->standby) { ret = start_output_stream(out); if (ret != 0) { pthread_mutex_unlock(&adev->lock); goto exit; } out->standby = false; } pthread_mutex_unlock(&adev->lock); if (out->disabled) { ret = -EPIPE; goto exit; } if (out->muted) memset((void *)buffer, 0, bytes); /* Write to all active PCMs */ for (i = 0; i < PCM_TOTAL; i++) if (out->pcm[i]) { ret = pcm_write(out->pcm[i], (void *)buffer, bytes); if (ret != 0) break; } if (ret == 0) out->written += bytes / (out->config.channels * sizeof(short)); exit: pthread_mutex_unlock(&out->lock); if (ret != 0) { usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) / out_get_sample_rate(&stream->common)); } 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; } static int out_get_next_write_timestamp(const struct audio_stream_out *stream, int64_t *timestamp) { return -EINVAL; } 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; pthread_mutex_lock(&out->lock); int i; // There is a question how to implement this correctly when there is more than one PCM stream. // We are just interested in the frames pending for playback in the kernel buffer here, // not the total played since start. The current behavior should be safe because the // cases where both cards are active are marginal. for (i = 0; i < PCM_TOTAL; i++) if (out->pcm[i]) { size_t avail; if (pcm_get_htimestamp(out->pcm[i], &avail, timestamp) == 0) { size_t kernel_buffer_size = out->config.period_size * out->config.period_count; // FIXME This calculation is incorrect if there is buffering after app processor int64_t signed_frames = out->written - kernel_buffer_size + avail; // It would be unusual for this value to be negative, but check just in case ... if (signed_frames >= 0) { *frames = signed_frames; ret = 0; } break; } } pthread_mutex_unlock(&out->lock); return ret; } /** audio_stream_in implementation **/ static uint32_t in_get_sample_rate(const struct audio_stream *stream) { struct stream_in *in = (struct stream_in *)stream; return in->requested_rate; } static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) { return 0; } static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) { struct stream_in *in = (struct stream_in *)stream; return in->channel_mask; } static size_t in_get_buffer_size(const struct audio_stream *stream) { struct stream_in *in = (struct stream_in *)stream; return get_input_buffer_size(in->requested_rate, AUDIO_FORMAT_PCM_16_BIT, popcount(in_get_channels(stream))); } static audio_format_t in_get_format(const struct audio_stream *stream) { return AUDIO_FORMAT_PCM_16_BIT; } static int in_set_format(struct audio_stream *stream, audio_format_t format) { return -ENOSYS; } static int do_in_standby(struct stream_in *in) { struct audio_device *adev = in->dev; if (!in->standby) { pcm_close(in->pcm); in->pcm = NULL; if (in->device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) stop_bt_sco(adev); in->dev->input_source = AUDIO_SOURCE_DEFAULT; in->dev->in_device = AUDIO_DEVICE_NONE; in->dev->in_channel_mask = 0; select_devices(adev); in->standby = true; if (get_bubblelevel(adev)) in->dev->bubble_level->stop_polling(adev->bubble_level); } eS305_SetActiveIoHandle(ES305_IO_HANDLE_NONE); return 0; } static int in_standby(struct audio_stream *stream) { struct stream_in *in = (struct stream_in *)stream; int ret; pthread_mutex_lock(&in->dev->lock); pthread_mutex_lock(&in->lock); ret = do_in_standby(in); pthread_mutex_unlock(&in->lock); pthread_mutex_unlock(&in->dev->lock); return ret; } 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 stream_in *in = (struct stream_in *)stream; struct audio_device *adev = in->dev; struct str_parms *parms; char value[32]; int ret; unsigned int val; bool apply_now = false; parms = str_parms_create_str(kvpairs); pthread_mutex_lock(&adev->lock); pthread_mutex_lock(&in->lock); ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, sizeof(value)); if (ret >= 0) { val = atoi(value); /* no audio source uses val == 0 */ if ((in->input_source != val) && (val != 0)) { in->input_source = val; apply_now = !in->standby; } } ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); if (ret >= 0) { /* strip AUDIO_DEVICE_BIT_IN to allow bitwise comparisons */ val = atoi(value) & ~AUDIO_DEVICE_BIT_IN; /* no audio device uses val == 0 */ if ((in->device != val) && (val != 0)) { /* force output standby to start or stop SCO pcm stream if needed */ if ((val & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) ^ (in->device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) { do_in_standby(in); } in->device = val; apply_now = !in->standby; } } if (apply_now) { adev->input_source = in->input_source; adev->in_device = in->device; select_devices(adev); } 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 in_apply_ramp(struct stream_in *in, int16_t *buffer, size_t frames) { size_t i; uint16_t vol = in->ramp_vol; uint16_t step = in->ramp_step; frames = (frames < in->ramp_frames) ? frames : in->ramp_frames; if (in->channel_mask == AUDIO_CHANNEL_IN_MONO) for (i = 0; i < frames; i++) { buffer[i] = (int16_t)((buffer[i] * vol) >> 16); vol += step; } else for (i = 0; i < frames; i++) { buffer[2*i] = (int16_t)((buffer[2*i] * vol) >> 16); buffer[2*i + 1] = (int16_t)((buffer[2*i + 1] * vol) >> 16); vol += step; } in->ramp_vol = vol; in->ramp_frames -= frames; } 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; 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 in_set_parameters() 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 */ ret = read_frames(in, buffer, frames_rq); if (ret > 0) ret = 0; if (in->ramp_frames > 0) in_apply_ramp(in, buffer, frames_rq); /* * 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: 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 stream_in *in = (struct stream_in *)stream; effect_descriptor_t descr; if ((*effect)->get_descriptor(effect, &descr) == 0) { pthread_mutex_lock(&in->dev->lock); pthread_mutex_lock(&in->lock); eS305_AddEffect(&descr, in->io_handle); pthread_mutex_unlock(&in->lock); pthread_mutex_unlock(&in->dev->lock); } return 0; } static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { struct stream_in *in = (struct stream_in *)stream; effect_descriptor_t descr; if ((*effect)->get_descriptor(effect, &descr) == 0) { pthread_mutex_lock(&in->dev->lock); pthread_mutex_lock(&in->lock); eS305_RemoveEffect(&descr, in->io_handle); pthread_mutex_unlock(&in->lock); pthread_mutex_unlock(&in->dev->lock); } return 0; } static int adev_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out) { struct audio_device *adev = (struct audio_device *)dev; struct stream_out *out; int ret; enum output_type type; out = (struct stream_out *)calloc(1, sizeof(struct stream_out)); if (!out) return -ENOMEM; out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO; out->channel_mask = AUDIO_CHANNEL_OUT_STEREO; if (devices == AUDIO_DEVICE_NONE) devices = AUDIO_DEVICE_OUT_SPEAKER; out->device = devices; if (flags & AUDIO_OUTPUT_FLAG_DIRECT && devices == AUDIO_DEVICE_OUT_AUX_DIGITAL) { pthread_mutex_lock(&adev->lock); ret = read_hdmi_channel_masks(adev, out); pthread_mutex_unlock(&adev->lock); if (ret != 0) goto err_open; if (config->sample_rate == 0) config->sample_rate = HDMI_MULTI_DEFAULT_SAMPLING_RATE; if (config->channel_mask == 0) config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1; out->channel_mask = config->channel_mask; out->config = pcm_config_hdmi_multi; out->config.rate = config->sample_rate; out->config.channels = popcount(config->channel_mask); out->pcm_device = PCM_DEVICE; type = OUTPUT_HDMI; } else if (flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) { out->config = pcm_config_deep; out->pcm_device = PCM_DEVICE_DEEP; type = OUTPUT_DEEP_BUF; } else { out->config = pcm_config; out->pcm_device = PCM_DEVICE; type = OUTPUT_LOW_LATENCY; } 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->dev = adev; 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); out->standby = true; /* out->muted = false; by calloc() */ /* out->written = 0; by calloc() */ pthread_mutex_lock(&adev->lock); if (adev->outputs[type]) { pthread_mutex_unlock(&adev->lock); ret = -EBUSY; goto err_open; } adev->outputs[type] = out; pthread_mutex_unlock(&adev->lock); *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 audio_device *adev; enum output_type type; out_standby(&stream->common); adev = (struct audio_device *)dev; pthread_mutex_lock(&adev->lock); for (type = 0; type < OUTPUT_TOTAL; ++type) { if (adev->outputs[type] == (struct stream_out *) stream) { adev->outputs[type] = NULL; break; } } pthread_mutex_unlock(&adev->lock); free(stream); } static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) { return 0; } static char * adev_get_parameters(const struct audio_hw_device *dev, const char *keys) { struct audio_device *adev = (struct audio_device *)dev; struct str_parms *parms = str_parms_create_str(keys); char value[32]; int ret = str_parms_get_str(parms, "ec_supported", value, sizeof(value)); char *str; str_parms_destroy(parms); if (ret >= 0) { parms = str_parms_create_str("ec_supported=yes"); str = str_parms_to_str(parms); str_parms_destroy(parms); return str; } 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) { return -ENOSYS; } 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, audio_mode_t mode) { return 0; } static int adev_set_mic_mute(struct audio_hw_device *dev, bool 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) { 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, const struct audio_config *config) { return get_input_buffer_size(config->sample_rate, config->format, popcount(config->channel_mask)); } static int adev_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in) { struct audio_device *adev = (struct audio_device *)dev; struct stream_in *in; int ret; *stream_in = NULL; /* Respond with a request for mono if a different format is given. */ if (config->channel_mask != AUDIO_CHANNEL_IN_MONO && config->channel_mask != AUDIO_CHANNEL_IN_FRONT_BACK) { config->channel_mask = AUDIO_CHANNEL_IN_MONO; return -EINVAL; } in = (struct stream_in *)calloc(1, sizeof(struct 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->dev = adev; in->standby = true; in->requested_rate = config->sample_rate; in->input_source = AUDIO_SOURCE_DEFAULT; /* strip AUDIO_DEVICE_BIT_IN to allow bitwise comparisons */ in->device = devices & ~AUDIO_DEVICE_BIT_IN; in->io_handle = handle; in->channel_mask = config->channel_mask; in->buffer = malloc(pcm_config_in.period_size * pcm_config_in.channels * audio_stream_frame_size(&in->stream.common)); if (!in->buffer) { ret = -ENOMEM; goto err_malloc; } if (in->requested_rate != pcm_config_in.rate) { in->buf_provider.get_next_buffer = get_next_buffer; in->buf_provider.release_buffer = release_buffer; ret = create_resampler(pcm_config_in.rate, in->requested_rate, popcount(in->channel_mask), RESAMPLER_QUALITY_DEFAULT, &in->buf_provider, &in->resampler); if (ret != 0) { ret = -EINVAL; goto err_resampler; } } *stream_in = &in->stream; return 0; err_resampler: free(in->buffer); err_malloc: free(in); return ret; } static void adev_close_input_stream(struct audio_hw_device *dev, struct audio_stream_in *stream) { struct stream_in *in = (struct stream_in *)stream; in_standby(&stream->common); if (in->resampler) { release_resampler(in->resampler); in->resampler = NULL; } free(in->buffer); free(stream); } static int adev_dump(const audio_hw_device_t *device, int fd) { return 0; } static int adev_close(hw_device_t *device) { struct audio_device *adev = (struct audio_device *)device; audio_route_free(adev->ar); eS305_Release(); if (adev->hdmi_drv_fd >= 0) close(adev->hdmi_drv_fd); if (adev->bubble_level) bubble_level_release(adev->bubble_level); free(device); return 0; } static int adev_open(const hw_module_t* module, const char* name, hw_device_t** device) { struct audio_device *adev; int ret; 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.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->ar = audio_route_init(MIXER_CARD, NULL); adev->input_source = AUDIO_SOURCE_DEFAULT; /* adev->cur_route_id initial value is 0 and such that first device * selection is always applied by select_devices() */ adev->es305_preset = ES305_PRESET_INIT; adev->es305_new_mode = ES305_MODE_LEVEL; adev->es305_mode = ES305_MODE_LEVEL; adev->hdmi_drv_fd = -1; *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, .module_api_version = AUDIO_MODULE_API_VERSION_0_1, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = AUDIO_HARDWARE_MODULE_ID, .name = "Manta audio HW HAL", .author = "The Android Open Source Project", .methods = &hal_module_methods, }, };