/* * Copyright (C) 2012 Paul Kocialkowski * * 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 . */ #define LOG_TAG "TinyALSA-Audio Hardware" #include #include #include #include #include #include #include #ifdef YAMAHA_MC1N2_AUDIO #include #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); } 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); 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; 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); #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); } 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); } 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); return 0; error_params: str_parms_destroy(parms); 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; int channel_count; size_t size; ALOGD("%s(%p, %p)", __func__, dev, config); if(dev == NULL || config == 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; channel_count = popcount(config->channel_mask); 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); 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); } 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); return 0; error_device: *device = NULL; free(tinyalsa_audio_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, }, };