/* * 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 <cutils/log.h> #include <cutils/properties.h> #include <cutils/str_parms.h> #include <hardware/audio.h> #include <hardware/hardware.h> #include <system/audio.h> #include <tinyalsa/asoundlib.h> #include <audio_utils/resampler.h> #include "audio_route.h" #define PCM_CARD 1 #define PCM_DEVICE 0 #define PCM_DEVICE_SCO 2 #define OUT_PERIOD_SIZE 512 #define OUT_SHORT_PERIOD_COUNT 2 #define OUT_LONG_PERIOD_COUNT 8 #define OUT_SAMPLING_RATE 44100 #define IN_PERIOD_SIZE 1024 #define IN_PERIOD_COUNT 4 #define IN_SAMPLING_RATE 44100 #define SCO_PERIOD_SIZE 256 #define SCO_PERIOD_COUNT 4 #define SCO_SAMPLING_RATE 8000 /* minimum sleep time in out_write() when write threshold is not reached */ #define MIN_WRITE_SLEEP_US 2000 #define MAX_WRITE_SLEEP_US ((OUT_PERIOD_SIZE * OUT_SHORT_PERIOD_COUNT * 1000000) \ / OUT_SAMPLING_RATE) enum { OUT_BUFFER_TYPE_UNKNOWN, OUT_BUFFER_TYPE_SHORT, OUT_BUFFER_TYPE_LONG, }; struct pcm_config pcm_config_out = { .channels = 2, .rate = OUT_SAMPLING_RATE, .period_size = OUT_PERIOD_SIZE, .period_count = OUT_LONG_PERIOD_COUNT, .format = PCM_FORMAT_S16_LE, .start_threshold = OUT_PERIOD_SIZE * OUT_SHORT_PERIOD_COUNT, }; struct pcm_config pcm_config_in = { .channels = 2, .rate = IN_SAMPLING_RATE, .period_size = IN_PERIOD_SIZE, .period_count = IN_PERIOD_COUNT, .format = PCM_FORMAT_S16_LE, .start_threshold = 1, .stop_threshold = (IN_PERIOD_SIZE * IN_PERIOD_COUNT), }; struct pcm_config pcm_config_sco = { .channels = 1, .rate = SCO_SAMPLING_RATE, .period_size = SCO_PERIOD_SIZE, .period_count = SCO_PERIOD_COUNT, .format = PCM_FORMAT_S16_LE, }; struct audio_device { struct audio_hw_device hw_device; pthread_mutex_t lock; /* see note below on mutex acquisition order */ unsigned int out_device; unsigned int in_device; bool standby; bool mic_mute; struct audio_route *ar; int orientation; bool screen_off; struct stream_out *active_out; struct stream_in *active_in; }; struct stream_out { struct audio_stream_out stream; pthread_mutex_t lock; /* see note below on mutex acquisition order */ struct pcm *pcm; struct pcm_config *pcm_config; bool standby; struct resampler_itfe *resampler; int16_t *buffer; size_t buffer_frames; int write_threshold; int cur_write_threshold; int buffer_type; 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; struct pcm_config *pcm_config; bool standby; unsigned int requested_rate; struct resampler_itfe *resampler; struct resampler_buffer_provider buf_provider; int16_t *buffer; size_t buffer_size; size_t frames_in; int read_status; struct audio_device *dev; }; enum { ORIENTATION_LANDSCAPE, ORIENTATION_PORTRAIT, ORIENTATION_SQUARE, ORIENTATION_UNDEFINED, }; static uint32_t out_get_sample_rate(const struct audio_stream *stream); static size_t out_get_buffer_size(const struct audio_stream *stream); static audio_format_t out_get_format(const struct audio_stream *stream); static uint32_t in_get_sample_rate(const struct audio_stream *stream); static size_t in_get_buffer_size(const struct audio_stream *stream); static audio_format_t in_get_format(const struct audio_stream *stream); static int get_next_buffer(struct resampler_buffer_provider *buffer_provider, struct resampler_buffer* buffer); static void release_buffer(struct resampler_buffer_provider *buffer_provider, struct resampler_buffer* buffer); /* * NOTE: when multiple mutexes have to be acquired, always take the * audio_device mutex first, followed by the stream_in and/or * stream_out mutexes. */ /* Helper functions */ static void select_devices(struct audio_device *adev) { int headphone_on; int speaker_on; int docked; int main_mic_on; headphone_on = adev->out_device & (AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE); speaker_on = adev->out_device & AUDIO_DEVICE_OUT_SPEAKER; docked = adev->out_device & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; main_mic_on = adev->in_device & AUDIO_DEVICE_IN_BUILTIN_MIC; reset_mixer_state(adev->ar); if (speaker_on) audio_route_apply_path(adev->ar, "speaker"); if (headphone_on) audio_route_apply_path(adev->ar, "headphone"); if (docked) audio_route_apply_path(adev->ar, "dock"); if (main_mic_on) { if (adev->orientation == ORIENTATION_LANDSCAPE) audio_route_apply_path(adev->ar, "main-mic-left"); else audio_route_apply_path(adev->ar, "main-mic-top"); } update_mixer_state(adev->ar); ALOGV("hp=%c speaker=%c dock=%c main-mic=%c", headphone_on ? 'y' : 'n', speaker_on ? 'y' : 'n', docked ? 'y' : 'n', main_mic_on ? 'y' : 'n'); } /* must be called with hw device and output stream mutexes locked */ static void do_out_standby(struct stream_out *out) { struct audio_device *adev = out->dev; if (!out->standby) { pcm_close(out->pcm); out->pcm = NULL; adev->active_out = NULL; if (out->resampler) { release_resampler(out->resampler); out->resampler = NULL; } if (out->buffer) { free(out->buffer); out->buffer = NULL; } out->standby = true; } } /* must be called with hw device and input stream mutexes locked */ static void do_in_standby(struct stream_in *in) { struct audio_device *adev = in->dev; if (!in->standby) { pcm_close(in->pcm); in->pcm = NULL; adev->active_in = NULL; if (in->resampler) { release_resampler(in->resampler); in->resampler = NULL; } if (in->buffer) { free(in->buffer); in->buffer = NULL; } in->standby = true; } } /* 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; unsigned int device; int ret; /* * Due to the lack of sample rate converters in the SoC, * it greatly simplifies things to have only the main * (speaker/headphone) PCM or the BC SCO PCM open at * the same time. */ if (adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO) { device = PCM_DEVICE_SCO; out->pcm_config = &pcm_config_sco; } else { device = PCM_DEVICE; out->pcm_config = &pcm_config_out; out->buffer_type = OUT_BUFFER_TYPE_UNKNOWN; } /* * All open PCMs can only use a single group of rates at once: * Group 1: 11.025, 22.05, 44.1 * Group 2: 8, 16, 32, 48 * Group 1 is used for digital audio playback since 44.1 is * the most common rate, but group 2 is required for SCO. */ if (adev->active_in) { struct stream_in *in = adev->active_in; pthread_mutex_lock(&in->lock); if (((out->pcm_config->rate % 8000 == 0) && (in->pcm_config->rate % 8000) != 0) || ((out->pcm_config->rate % 11025 == 0) && (in->pcm_config->rate % 11025) != 0)) do_in_standby(in); pthread_mutex_unlock(&in->lock); } out->pcm = pcm_open(PCM_CARD, device, PCM_OUT | PCM_NORESTART, out->pcm_config); if (out->pcm && !pcm_is_ready(out->pcm)) { ALOGE("pcm_open(out) failed: %s", pcm_get_error(out->pcm)); pcm_close(out->pcm); return -ENOMEM; } /* * If the stream rate differs from the PCM rate, we need to * create a resampler. */ if (out_get_sample_rate(&out->stream.common) != out->pcm_config->rate) { ret = create_resampler(out_get_sample_rate(&out->stream.common), out->pcm_config->rate, out->pcm_config->channels, RESAMPLER_QUALITY_DEFAULT, NULL, &out->resampler); out->buffer_frames = (pcm_config_out.period_size * out->pcm_config->rate) / out_get_sample_rate(&out->stream.common) + 1; out->buffer = malloc(pcm_frames_to_bytes(out->pcm, out->buffer_frames)); } adev->active_out = out; 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; unsigned int device; int ret; /* * Due to the lack of sample rate converters in the SoC, * it greatly simplifies things to have only the main * mic PCM or the BC SCO PCM open at the same time. */ if (adev->in_device & AUDIO_DEVICE_IN_ALL_SCO) { device = PCM_DEVICE_SCO; in->pcm_config = &pcm_config_sco; } else { device = PCM_DEVICE; in->pcm_config = &pcm_config_in; } /* * All open PCMs can only use a single group of rates at once: * Group 1: 11.025, 22.05, 44.1 * Group 2: 8, 16, 32, 48 * Group 1 is used for digital audio playback since 44.1 is * the most common rate, but group 2 is required for SCO. */ if (adev->active_out) { struct stream_out *out = adev->active_out; pthread_mutex_lock(&out->lock); if (((in->pcm_config->rate % 8000 == 0) && (out->pcm_config->rate % 8000) != 0) || ((in->pcm_config->rate % 11025 == 0) && (out->pcm_config->rate % 11025) != 0)) do_out_standby(out); pthread_mutex_unlock(&out->lock); } in->pcm = pcm_open(PCM_CARD, device, PCM_IN, in->pcm_config); if (in->pcm && !pcm_is_ready(in->pcm)) { ALOGE("pcm_open(in) failed: %s", pcm_get_error(in->pcm)); pcm_close(in->pcm); return -ENOMEM; } /* * If the stream rate differs from the PCM rate, we need to * create a resampler. */ if (in_get_sample_rate(&in->stream.common) != in->pcm_config->rate) { in->buf_provider.get_next_buffer = get_next_buffer; in->buf_provider.release_buffer = release_buffer; ret = create_resampler(in->pcm_config->rate, in_get_sample_rate(&in->stream.common), 1, RESAMPLER_QUALITY_DEFAULT, &in->buf_provider, &in->resampler); } in->buffer_size = pcm_frames_to_bytes(in->pcm, in->pcm_config->period_size); in->buffer = malloc(in->buffer_size); in->frames_in = 0; adev->active_in = in; return 0; } static int get_next_buffer(struct resampler_buffer_provider *buffer_provider, struct resampler_buffer* buffer) { struct stream_in *in; 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, in->buffer_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 = in->pcm_config->period_size; if (in->pcm_config->channels == 2) { unsigned int i; /* Discard right channel */ 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 + (in->pcm_config->period_size - in->frames_in); 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; 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; } /* API functions */ static uint32_t out_get_sample_rate(const struct audio_stream *stream) { return pcm_config_out.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) { return pcm_config_out.period_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 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; } static int out_standby(struct audio_stream *stream) { struct stream_out *out = (struct stream_out *)stream; pthread_mutex_lock(&out->dev->lock); pthread_mutex_lock(&out->lock); do_out_standby(out); pthread_mutex_unlock(&out->lock); pthread_mutex_unlock(&out->dev->lock); return 0; } static int out_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); if (ret >= 0) { val = atoi(value); if ((adev->out_device != val) && (val != 0)) { /* * If SCO is turned on/off, we need to put audio into standby * because SCO uses a different PCM. */ if ((val & AUDIO_DEVICE_OUT_ALL_SCO) ^ (adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO)) { pthread_mutex_lock(&out->lock); do_out_standby(out); pthread_mutex_unlock(&out->lock); } adev->out_device = val; select_devices(adev); } } 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 stream_out *out = (struct stream_out *)stream; struct audio_device *adev = out->dev; size_t period_count; pthread_mutex_lock(&adev->lock); if (adev->screen_off && !adev->active_in && !(adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO)) period_count = OUT_LONG_PERIOD_COUNT; else period_count = OUT_SHORT_PERIOD_COUNT; pthread_mutex_unlock(&adev->lock); return (pcm_config_out.period_size * period_count * 1000) / pcm_config_out.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 = 0; struct stream_out *out = (struct stream_out *)stream; struct audio_device *adev = out->dev; size_t frame_size = audio_stream_frame_size(&out->stream.common); int16_t *in_buffer = (int16_t *)buffer; size_t in_frames = bytes / frame_size; size_t out_frames; int buffer_type; int kernel_frames; bool sco_on; /* * 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; } buffer_type = (adev->screen_off && !adev->active_in) ? OUT_BUFFER_TYPE_LONG : OUT_BUFFER_TYPE_SHORT; sco_on = (adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO); pthread_mutex_unlock(&adev->lock); /* detect changes in screen ON/OFF state and adapt buffer size * if needed. Do not change buffer size when routed to SCO device. */ if (!sco_on && (buffer_type != out->buffer_type)) { size_t period_count; if (buffer_type == OUT_BUFFER_TYPE_LONG) period_count = OUT_LONG_PERIOD_COUNT; else period_count = OUT_SHORT_PERIOD_COUNT; out->write_threshold = out->pcm_config->period_size * period_count; /* reset current threshold if exiting standby */ if (out->buffer_type == OUT_BUFFER_TYPE_UNKNOWN) out->cur_write_threshold = out->write_threshold; out->buffer_type = buffer_type; } /* Reduce number of channels, if necessary */ if (popcount(out_get_channels(&stream->common)) > (int)out->pcm_config->channels) { unsigned int i; /* Discard right channel */ for (i = 1; i < in_frames; i++) in_buffer[i] = in_buffer[i * 2]; /* The frame size is now half */ frame_size /= 2; } /* Change sample rate, if necessary */ if (out_get_sample_rate(&stream->common) != out->pcm_config->rate) { out_frames = out->buffer_frames; out->resampler->resample_from_input(out->resampler, in_buffer, &in_frames, out->buffer, &out_frames); in_buffer = out->buffer; } else { out_frames = in_frames; } if (!sco_on) { int total_sleep_time_us = 0; size_t period_size = out->pcm_config->period_size; /* do not allow more than out->cur_write_threshold frames in kernel * pcm driver buffer */ do { struct timespec time_stamp; if (pcm_get_htimestamp(out->pcm, (unsigned int *)&kernel_frames, &time_stamp) < 0) break; kernel_frames = pcm_get_buffer_size(out->pcm) - kernel_frames; if (kernel_frames > out->cur_write_threshold) { int sleep_time_us = (int)(((int64_t)(kernel_frames - out->cur_write_threshold) * 1000000) / out->pcm_config->rate); if (sleep_time_us < MIN_WRITE_SLEEP_US) break; total_sleep_time_us += sleep_time_us; if (total_sleep_time_us > MAX_WRITE_SLEEP_US) { ALOGW("out_write() limiting sleep time %d to %d", total_sleep_time_us, MAX_WRITE_SLEEP_US); sleep_time_us = MAX_WRITE_SLEEP_US - (total_sleep_time_us - sleep_time_us); } usleep(sleep_time_us); } } while ((kernel_frames > out->cur_write_threshold) && (total_sleep_time_us <= MAX_WRITE_SLEEP_US)); /* do not allow abrupt changes on buffer size. Increasing/decreasing * the threshold by steps of 1/4th of the buffer size keeps the write * time within a reasonable range during transitions. * Also reset current threshold just above current filling status when * kernel buffer is really depleted to allow for smooth catching up with * target threshold. */ if (out->cur_write_threshold > out->write_threshold) { out->cur_write_threshold -= period_size / 4; if (out->cur_write_threshold < out->write_threshold) { out->cur_write_threshold = out->write_threshold; } } else if (out->cur_write_threshold < out->write_threshold) { out->cur_write_threshold += period_size / 4; if (out->cur_write_threshold > out->write_threshold) { out->cur_write_threshold = out->write_threshold; } } else if ((kernel_frames < out->write_threshold) && ((out->write_threshold - kernel_frames) > (int)(period_size * OUT_SHORT_PERIOD_COUNT))) { out->cur_write_threshold = (kernel_frames / period_size + 1) * period_size; out->cur_write_threshold += period_size / 4; } } ret = pcm_write(out->pcm, in_buffer, out_frames * frame_size); if (ret == -EPIPE) { /* In case of underrun, don't sleep since we want to catch up asap */ pthread_mutex_unlock(&out->lock); return ret; } 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; } /** 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 size_t in_get_buffer_size(const struct audio_stream *stream) { struct stream_in *in = (struct stream_in *)stream; size_t size; /* * take resampling into account and return the closest majoring * multiple of 16 frames, as audioflinger expects audio buffers to * be a multiple of 16 frames */ size = (in->pcm_config->period_size * in_get_sample_rate(stream)) / in->pcm_config->rate; size = ((size + 15) / 16) * 16; return size * audio_stream_frame_size((struct audio_stream *)stream); } static uint32_t in_get_channels(const struct audio_stream *stream) { return AUDIO_CHANNEL_IN_MONO; } 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 in_standby(struct audio_stream *stream) { struct stream_in *in = (struct stream_in *)stream; pthread_mutex_lock(&in->dev->lock); pthread_mutex_lock(&in->lock); do_in_standby(in); pthread_mutex_unlock(&in->lock); pthread_mutex_unlock(&in->dev->lock); return 0; } static int in_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; parms = str_parms_create_str(kvpairs); ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); pthread_mutex_lock(&adev->lock); if (ret >= 0) { val = atoi(value) & ~AUDIO_DEVICE_BIT_IN; if ((adev->in_device != val) && (val != 0)) { /* * If SCO is turned on/off, we need to put audio into standby * because SCO uses a different PCM. */ if ((val & AUDIO_DEVICE_IN_ALL_SCO) ^ (adev->in_device & AUDIO_DEVICE_IN_ALL_SCO)) { pthread_mutex_lock(&in->lock); do_in_standby(in); pthread_mutex_unlock(&in->lock); } adev->in_device = val; select_devices(adev); } } 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 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 */if (in->resampler != NULL) { ret = read_frames(in, buffer, frames_rq); } else if (in->pcm_config->channels == 2) { /* * If the PCM is stereo, capture twice as many frames and * discard the right channel. */ unsigned int i; int16_t *in_buffer = (int16_t *)buffer; ret = pcm_read(in->pcm, in->buffer, bytes * 2); /* Discard right channel */ for (i = 0; i < frames_rq; i++) in_buffer[i] = in->buffer[i * 2]; } else { ret = pcm_read(in->pcm, buffer, bytes); } if (ret > 0) ret = 0; /* * Instead of writing zeroes here, we could trust the hardware * to always provide zeroes when muted. */ if (ret == 0 && adev->mic_mute) memset(buffer, 0, bytes); exit: 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) { return 0; } static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { 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; out = (struct stream_out *)calloc(1, sizeof(struct stream_out)); if (!out) return -ENOMEM; out->stream.common.get_sample_rate = out_get_sample_rate; out->stream.common.set_sample_rate = out_set_sample_rate; out->stream.common.get_buffer_size = out_get_buffer_size; out->stream.common.get_channels = out_get_channels; out->stream.common.get_format = out_get_format; out->stream.common.set_format = out_set_format; out->stream.common.standby = out_standby; out->stream.common.dump = out_dump; out->stream.common.set_parameters = out_set_parameters; out->stream.common.get_parameters = out_get_parameters; out->stream.common.add_audio_effect = out_add_audio_effect; out->stream.common.remove_audio_effect = out_remove_audio_effect; out->stream.get_latency = out_get_latency; out->stream.set_volume = out_set_volume; out->stream.write = out_write; out->stream.get_render_position = out_get_render_position; out->stream.get_next_write_timestamp = out_get_next_write_timestamp; out->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; *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) { out_standby(&stream->common); free(stream); } static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) { struct audio_device *adev = (struct 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, "orientation", value, sizeof(value)); if (ret >= 0) { int orientation; if (strcmp(value, "landscape") == 0) orientation = ORIENTATION_LANDSCAPE; else if (strcmp(value, "portrait") == 0) orientation = ORIENTATION_PORTRAIT; else if (strcmp(value, "square") == 0) orientation = ORIENTATION_SQUARE; else orientation = ORIENTATION_UNDEFINED; pthread_mutex_lock(&adev->lock); if (orientation != adev->orientation) { adev->orientation = orientation; /* * Orientation changes can occur with the input device * closed so we must call select_devices() here to set * up the mixer. This is because select_devices() will * not be called when the input device is opened if no * other input parameter is changed. */ select_devices(adev); } pthread_mutex_unlock(&adev->lock); } ret = str_parms_get_str(parms, "screen_state", value, sizeof(value)); if (ret >= 0) { if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) adev->screen_off = false; else adev->screen_off = true; } 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) { 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) { size_t size; /* * take resampling into account and return the closest majoring * multiple of 16 frames, as audioflinger expects audio buffers to * be a multiple of 16 frames */ size = (pcm_config_in.period_size * config->sample_rate) / pcm_config_in.rate; size = ((size + 15) / 16) * 16; return (size * popcount(config->channel_mask) * audio_bytes_per_sample(config->format)); } static int adev_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, 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_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->pcm_config = &pcm_config_in; /* default PCM config */ *stream_in = &in->stream; return 0; } 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); 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); 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(); adev->orientation = ORIENTATION_UNDEFINED; adev->out_device = AUDIO_DEVICE_OUT_SPEAKER; adev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC & ~AUDIO_DEVICE_BIT_IN; *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 = "Grouper audio HW HAL", .author = "The Android Open Source Project", .methods = &hal_module_methods, }, };