605c54b82e
Change-Id: I92348574296ab03c0f57b0dfa81bcded7ddb89cb
517 lines
13 KiB
C
517 lines
13 KiB
C
/*
|
|
* Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
|
|
*
|
|
* This is based on Galaxy Nexus audio.primary.tuna implementation:
|
|
* Copyright 2011, The Android Open-Source Project
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define LOG_TAG "TinyALSA-Audio Hardware"
|
|
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <cutils/str_parms.h>
|
|
#include <cutils/log.h>
|
|
|
|
#ifdef YAMAHA_MC1N2_AUDIO
|
|
#include <yamaha-mc1n2-audio.h>
|
|
#endif
|
|
|
|
#include "audio_hw.h"
|
|
#include "mixer.h"
|
|
|
|
/*
|
|
* Functions
|
|
*/
|
|
|
|
static int audio_hw_init_check(const struct audio_hw_device *dev)
|
|
{
|
|
struct tinyalsa_audio_device *device;
|
|
|
|
ALOGD("%s(%p)", __func__, dev);
|
|
|
|
if(dev == NULL)
|
|
return -1;
|
|
|
|
device = (struct tinyalsa_audio_device *) dev;
|
|
|
|
if(device->mixer == NULL)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int audio_hw_set_voice_volume(struct audio_hw_device *dev, float volume)
|
|
{
|
|
struct tinyalsa_audio_device *device;
|
|
audio_devices_t device_modem;
|
|
|
|
ALOGD("%s(%p, %f)++", __func__, dev, volume);
|
|
|
|
if(dev == NULL)
|
|
return -1;
|
|
|
|
device = (struct tinyalsa_audio_device *) dev;
|
|
|
|
if(device->mixer == NULL)
|
|
return -1;
|
|
|
|
if(volume != device->voice_volume) {
|
|
pthread_mutex_lock(&device->lock);
|
|
|
|
if(device->mode == AUDIO_MODE_IN_CALL) {
|
|
if(device->ril_interface != NULL)
|
|
device_modem = device->ril_interface->device_current;
|
|
else if(device->stream_out != NULL)
|
|
device_modem = device->stream_out->device_current;
|
|
else
|
|
device_modem = AUDIO_DEVICE_OUT_EARPIECE;
|
|
|
|
tinyalsa_mixer_set_voice_volume(device->mixer,
|
|
device_modem, volume);
|
|
|
|
if(device->ril_interface != NULL)
|
|
audio_ril_interface_set_voice_volume(device->ril_interface, device_modem, volume);
|
|
}
|
|
|
|
device->voice_volume = volume;
|
|
|
|
pthread_mutex_unlock(&device->lock);
|
|
}
|
|
|
|
ALOGD("%s(%p, %f)--", __func__, dev, volume);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int audio_hw_set_master_volume(struct audio_hw_device *dev, float volume)
|
|
{
|
|
struct tinyalsa_audio_device *device;
|
|
|
|
ALOGD("%s(%p, %f)++", __func__, dev, volume);
|
|
|
|
if(dev == NULL)
|
|
return -1;
|
|
|
|
device = (struct tinyalsa_audio_device *) dev;
|
|
|
|
if(device->mixer == NULL)
|
|
return -1;
|
|
|
|
pthread_mutex_lock(&device->lock);
|
|
tinyalsa_mixer_set_master_volume(device->mixer, volume);
|
|
pthread_mutex_unlock(&device->lock);
|
|
|
|
ALOGD("%s(%p, %f)--", __func__, dev, volume);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int audio_hw_set_mode(struct audio_hw_device *dev, int mode)
|
|
{
|
|
struct tinyalsa_audio_ril_interface *ril_interface;
|
|
struct tinyalsa_audio_device *device;
|
|
audio_devices_t device_modem;
|
|
int rc;
|
|
|
|
ALOGD("%s(%p, %d)++", __func__, dev, mode);
|
|
|
|
if(dev == NULL)
|
|
return -1;
|
|
|
|
device = (struct tinyalsa_audio_device *) dev;
|
|
|
|
if(mode != device->mode) {
|
|
pthread_mutex_lock(&device->lock);
|
|
|
|
if(mode == AUDIO_MODE_IN_CALL) {
|
|
tinyalsa_mixer_set_modem_state(device->mixer, 1);
|
|
|
|
if(device->stream_out != NULL)
|
|
device_modem = device->stream_out->device_current;
|
|
else
|
|
device_modem = AUDIO_DEVICE_OUT_EARPIECE;
|
|
|
|
tinyalsa_mixer_set_device(device->mixer, device_modem);
|
|
|
|
#ifdef YAMAHA_MC1N2_AUDIO
|
|
rc = yamaha_mc1n2_audio_modem_start(device->mc1n2_pdata);
|
|
if(rc < 0) {
|
|
ALOGE("Failed to set Yamaha-MC1N2-Audio route");
|
|
}
|
|
#endif
|
|
|
|
rc = audio_ril_interface_open((struct audio_hw_device *) device, device_modem, &ril_interface);
|
|
if(rc < 0 || ril_interface == NULL) {
|
|
ALOGE("Failed to open RIL interface");
|
|
device->ril_interface = NULL;
|
|
} else {
|
|
device->ril_interface = ril_interface;
|
|
|
|
//Only enable dualmic for earpiece.
|
|
if(device_modem == AUDIO_DEVICE_OUT_EARPIECE)
|
|
audio_ril_interface_set_twomic(ril_interface,TWO_MIC_SOLUTION_ON);
|
|
|
|
if(device->voice_volume)
|
|
audio_ril_interface_set_voice_volume(ril_interface, device_modem, device->voice_volume);
|
|
}
|
|
} else if(device->mode == AUDIO_MODE_IN_CALL) {
|
|
tinyalsa_mixer_set_modem_state(device->mixer, 0);
|
|
|
|
/*
|
|
* Should be safe to ALWAYS disable it on exit
|
|
* But we should instrument secril-client to be sure
|
|
* when this is/isn't controlled - FIXME
|
|
*/
|
|
if(device->ril_interface != NULL) {
|
|
audio_ril_interface_set_twomic(device->ril_interface,TWO_MIC_SOLUTION_OFF);
|
|
}
|
|
|
|
#ifdef YAMAHA_MC1N2_AUDIO
|
|
rc = yamaha_mc1n2_audio_modem_stop(device->mc1n2_pdata);
|
|
if(rc < 0) {
|
|
ALOGE("Failed to set Yamaha-MC1N2-Audio route");
|
|
}
|
|
#endif
|
|
|
|
if(device->ril_interface != NULL) {
|
|
audio_ril_interface_close((struct audio_hw_device *) device, device->ril_interface);
|
|
}
|
|
}
|
|
|
|
device->mode = mode;
|
|
|
|
pthread_mutex_unlock(&device->lock);
|
|
}
|
|
|
|
ALOGD("%s(%p, %d)--", __func__, dev, mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int audio_hw_set_mic_mute(struct audio_hw_device *dev, bool state)
|
|
{
|
|
struct tinyalsa_audio_device *device;
|
|
audio_devices_t device_modem;
|
|
|
|
ALOGD("%s(%p, %d)++", __func__, dev, state);
|
|
|
|
if(dev == NULL)
|
|
return -1;
|
|
|
|
device = (struct tinyalsa_audio_device *) dev;
|
|
|
|
if(device->mixer == NULL)
|
|
return -1;
|
|
|
|
if(device->mic_mute != state) {
|
|
pthread_mutex_lock(&device->lock);
|
|
|
|
if(device->mode == AUDIO_MODE_IN_CALL) {
|
|
if(device->ril_interface != NULL)
|
|
device_modem = device->ril_interface->device_current;
|
|
else if(device->stream_out != NULL)
|
|
device_modem = device->stream_out->device_current;
|
|
else
|
|
device_modem = AUDIO_DEVICE_OUT_EARPIECE;
|
|
|
|
tinyalsa_mixer_set_mic_mute(device->mixer,
|
|
device_modem, state);
|
|
|
|
if(device->ril_interface != NULL)
|
|
audio_ril_interface_set_mic_mute(device->ril_interface, state);
|
|
} else {
|
|
if(device->stream_in != NULL) {
|
|
tinyalsa_mixer_set_mic_mute(device->mixer,
|
|
device->stream_in->device_current, state);
|
|
}
|
|
}
|
|
|
|
device->mic_mute = state;
|
|
|
|
pthread_mutex_unlock(&device->lock);
|
|
}
|
|
|
|
ALOGD("%s(%p, %d)--", __func__, dev, state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int audio_hw_get_mic_mute(const struct audio_hw_device *dev, bool *state)
|
|
{
|
|
struct tinyalsa_audio_device *device;
|
|
|
|
ALOGD("%s(%p, %p)", __func__, dev, state);
|
|
|
|
if(dev == NULL)
|
|
return -1;
|
|
|
|
device = (struct tinyalsa_audio_device *) dev;
|
|
|
|
*state = device->mic_mute;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int audio_hw_set_parameters(struct audio_hw_device *dev,
|
|
const char *kvpairs)
|
|
{
|
|
struct tinyalsa_audio_device *device;
|
|
struct str_parms *parms;
|
|
char value_string[32] = { 0 };
|
|
int value;
|
|
int rc;
|
|
|
|
ALOGD("%s(%p, %s)++", __func__, dev, kvpairs);
|
|
|
|
if(dev == NULL || kvpairs == NULL)
|
|
return -1;
|
|
|
|
device = (struct tinyalsa_audio_device *) dev;
|
|
|
|
if(device->mixer == NULL)
|
|
return -1;
|
|
|
|
parms = str_parms_create_str(kvpairs);
|
|
if(parms == NULL)
|
|
return -1;
|
|
|
|
rc = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value_string, sizeof(value_string));
|
|
if(rc < 0)
|
|
goto error_params;
|
|
|
|
value = atoi(value_string);
|
|
|
|
pthread_mutex_lock(&device->lock);
|
|
|
|
if(audio_is_output_device((audio_devices_t) value)) {
|
|
if(device->stream_out != NULL && device->stream_out->device_current != (audio_devices_t) value) {
|
|
pthread_mutex_lock(&device->stream_out->lock);
|
|
audio_out_set_route(device->stream_out, (audio_devices_t) value);
|
|
pthread_mutex_unlock(&device->stream_out->lock);
|
|
}
|
|
if(device->ril_interface != NULL && device->ril_interface->device_current != (audio_devices_t) value) {
|
|
audio_ril_interface_set_route(device->ril_interface, (audio_devices_t) value);
|
|
}
|
|
} else if(audio_is_input_device((audio_devices_t) value)) {
|
|
if(device->stream_in != NULL && device->stream_in->device_current != (audio_devices_t) value) {
|
|
pthread_mutex_lock(&device->stream_in->lock);
|
|
audio_in_set_route(device->stream_in, (audio_devices_t) value);
|
|
pthread_mutex_unlock(&device->stream_in->lock);
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&device->lock);
|
|
|
|
str_parms_destroy(parms);
|
|
|
|
ALOGD("%s(%p, %s)--", __func__, dev, kvpairs);
|
|
|
|
return 0;
|
|
|
|
error_params:
|
|
str_parms_destroy(parms);
|
|
|
|
ALOGD("%s(%p, %s)-- (PARAMETER ERROR)", __func__, dev, kvpairs);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static char *audio_hw_get_parameters(const struct audio_hw_device *dev,
|
|
const char *keys)
|
|
{
|
|
ALOGD("%s(%p, %s)", __func__, dev, keys);
|
|
|
|
return strdup("");
|
|
}
|
|
|
|
static size_t audio_hw_get_input_buffer_size(const struct audio_hw_device *dev,
|
|
const struct audio_config *config)
|
|
{
|
|
struct tinyalsa_audio_device *device;
|
|
struct tinyalsa_mixer_io_props *mixer_props;
|
|
size_t size;
|
|
|
|
int channel_count = popcount(config->channel_mask);
|
|
|
|
ALOGD("%s(%p, %d, %d, %d)++", __func__, dev, config->sample_rate, config->format, channel_count);
|
|
|
|
if(dev == NULL)
|
|
return -1;
|
|
|
|
device = (struct tinyalsa_audio_device *) dev;
|
|
|
|
if(device->mixer == NULL)
|
|
return -1;
|
|
|
|
mixer_props = tinyalsa_mixer_get_input_props(device->mixer);
|
|
if(mixer_props == NULL)
|
|
return -1;
|
|
|
|
// Default value
|
|
if(mixer_props->rate == 0)
|
|
mixer_props->rate = 44100;
|
|
|
|
size = (mixer_props->period_size * config->sample_rate) / mixer_props->rate;
|
|
size = ((size + 15) / 16) * 16;
|
|
size = size * channel_count * audio_bytes_per_sample(config->format);
|
|
|
|
ALOGD("%s(%p, %d, %d, %d)--", __func__, dev, config->sample_rate, config->format, channel_count);
|
|
|
|
return size;
|
|
}
|
|
|
|
static int audio_hw_dump(const audio_hw_device_t *device, int fd)
|
|
{
|
|
ALOGD("%s(%p, %d)", __func__, device, fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Interface
|
|
*/
|
|
|
|
int audio_hw_close(hw_device_t *device)
|
|
{
|
|
struct tinyalsa_audio_device *tinyalsa_audio_device;
|
|
|
|
ALOGD("%s(%p)++", __func__, device);
|
|
|
|
if(device != NULL) {
|
|
tinyalsa_audio_device = (struct tinyalsa_audio_device *) device;
|
|
|
|
if(tinyalsa_audio_device->mixer != NULL) {
|
|
tinyalsa_mixer_close(tinyalsa_audio_device->mixer);
|
|
tinyalsa_audio_device->mixer = NULL;
|
|
}
|
|
|
|
#ifdef YAMAHA_MC1N2_AUDIO
|
|
if(tinyalsa_audio_device->mc1n2_pdata != NULL) {
|
|
yamaha_mc1n2_audio_stop(tinyalsa_audio_device->mc1n2_pdata);
|
|
tinyalsa_audio_device->mc1n2_pdata = NULL;
|
|
}
|
|
#endif
|
|
|
|
free(device);
|
|
}
|
|
|
|
ALOGD("%s(%p)--", __func__, device);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int audio_hw_open(const hw_module_t *module, const char *name,
|
|
hw_device_t **device)
|
|
{
|
|
struct tinyalsa_audio_device *tinyalsa_audio_device = NULL;
|
|
struct tinyalsa_mixer *tinyalsa_mixer = NULL;
|
|
struct audio_hw_device *dev;
|
|
int rc;
|
|
|
|
ALOGD("%s(%p, %s, %p)++", __func__, module, name, device);
|
|
|
|
if(device == NULL)
|
|
return -EINVAL;
|
|
|
|
if(strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
|
|
return -EINVAL;
|
|
|
|
tinyalsa_audio_device = calloc(1, sizeof(struct tinyalsa_audio_device));
|
|
if(tinyalsa_audio_device == NULL)
|
|
return -ENOMEM;
|
|
|
|
dev = &(tinyalsa_audio_device->device);
|
|
|
|
dev->common.tag = HARDWARE_DEVICE_TAG;
|
|
dev->common.version = AUDIO_DEVICE_API_VERSION_2_0;
|
|
dev->common.module = (struct hw_module_t *) module;
|
|
dev->common.close = audio_hw_close;
|
|
|
|
dev->init_check = audio_hw_init_check;
|
|
dev->set_voice_volume = audio_hw_set_voice_volume;
|
|
dev->set_master_volume = audio_hw_set_master_volume;
|
|
dev->set_mode = audio_hw_set_mode;
|
|
dev->set_mic_mute = audio_hw_set_mic_mute;
|
|
dev->get_mic_mute = audio_hw_get_mic_mute;
|
|
dev->set_parameters = audio_hw_set_parameters;
|
|
dev->get_parameters = audio_hw_get_parameters;
|
|
dev->get_input_buffer_size = audio_hw_get_input_buffer_size;
|
|
|
|
dev->open_output_stream = audio_hw_open_output_stream;
|
|
dev->close_output_stream = audio_hw_close_output_stream;
|
|
|
|
dev->open_input_stream = audio_hw_open_input_stream;
|
|
dev->close_input_stream = audio_hw_close_input_stream;
|
|
|
|
dev->dump = audio_hw_dump;
|
|
|
|
#ifdef YAMAHA_MC1N2_AUDIO
|
|
rc = yamaha_mc1n2_audio_start(&tinyalsa_audio_device->mc1n2_pdata,
|
|
YAMAHA_MC1N2_AUDIO_DEVICE);
|
|
if(rc < 0) {
|
|
ALOGE("Failed to open Yamaha-MC1N2-Audio");
|
|
goto error_device;
|
|
}
|
|
|
|
rc = yamaha_mc1n2_audio_init(tinyalsa_audio_device->mc1n2_pdata);
|
|
if(rc < 0) {
|
|
ALOGE("Failed to init Yamaha-MC1N2-Audio");
|
|
}
|
|
#endif
|
|
|
|
rc = tinyalsa_mixer_open(&tinyalsa_mixer, TINYALSA_MIXER_CONFIG_FILE);
|
|
if(rc < 0 || tinyalsa_mixer == NULL) {
|
|
ALOGE("Failed to open mixer!");
|
|
goto error_device;
|
|
}
|
|
|
|
tinyalsa_audio_device->mixer = tinyalsa_mixer;
|
|
|
|
*device = &(dev->common);
|
|
|
|
ALOGD("%s(%p, %s, %p)--", __func__, module, name, device);
|
|
|
|
return 0;
|
|
|
|
error_device:
|
|
*device = NULL;
|
|
free(tinyalsa_audio_device);
|
|
|
|
ALOGD("%s(%p, %s, %p)-- (DEVICE ERROR)", __func__, module, name, device);
|
|
|
|
return -1;
|
|
}
|
|
|
|
struct hw_module_methods_t audio_hw_module_methods = {
|
|
.open = audio_hw_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 = "TinyALSA-Audio",
|
|
.author = "Paul Kocialkowski",
|
|
.methods = &audio_hw_module_methods,
|
|
},
|
|
};
|