/* * 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 #ifdef YAMAHA_MC1N2_AUDIO #include #endif #include "audio_hw.h" #include "mixer.h" /* * Functions */ static uint32_t audio_hw_get_supported_devices(const struct audio_hw_device *dev) { struct tinyalsa_audio_device *device; LOGD("%s(%p)", __func__, dev); if(dev == NULL) return -1; device = (struct tinyalsa_audio_device *) dev; if(device->mixer == NULL) return -1; return (uint32_t) tinyalsa_mixer_get_supported_devices(device->mixer); } static int audio_hw_init_check(const struct audio_hw_device *dev) { struct tinyalsa_audio_device *device; LOGD("%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; LOGD("%s(%p, %f)", __func__, dev, volume); if(dev == NULL) return -1; device = (struct tinyalsa_audio_device *) dev; if(device->mixer == NULL) return -1; if(device->mode == AUDIO_MODE_IN_CALL) { // FIXME: Select the device from ril interface tinyalsa_mixer_set_voice_volume(device->mixer, AUDIO_DEVICE_OUT_DEFAULT, volume); } return 0; } static int audio_hw_set_master_volume(struct audio_hw_device *dev, float volume) { struct tinyalsa_audio_device *device; LOGD("%s(%p, %f)", __func__, dev, volume); if(dev == NULL) return -1; device = (struct tinyalsa_audio_device *) dev; if(device->mixer == NULL) return -1; tinyalsa_mixer_set_master_volume(device->mixer, volume); return 0; } static int audio_hw_set_mode(struct audio_hw_device *dev, int mode) { struct tinyalsa_audio_device *device; int rc; LOGD("%s(%p, %d)", __func__, dev, mode); if(dev == NULL) return -1; device = (struct tinyalsa_audio_device *) dev; if(mode != device->mode) { if(mode == AUDIO_MODE_IN_CALL) { tinyalsa_mixer_set_modem_state(device->mixer, 1); if(device->stream_out != NULL) tinyalsa_mixer_set_device(device->mixer, device->stream_out->device_current); #ifdef YAMAHA_MC1N2_AUDIO rc = yamaha_mc1n2_audio_modem_start(device->mc1n2_pdata); if(rc < 0) { LOGE("Failed to set Yamaha-MC1N2-Audio route"); } #endif } 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) { LOGE("Failed to set Yamaha-MC1N2-Audio route"); } #endif } device->mode = mode; } return 0; } static int audio_hw_set_mic_mute(struct audio_hw_device *dev, bool state) { struct tinyalsa_audio_device *device; LOGD("%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) { device->mic_mute = state; // FIXME: Select the device from input if mode isn't in-call, // select it from ril interface if mode is in-call tinyalsa_mixer_set_mic_mute(device->mixer, AUDIO_DEVICE_IN_DEFAULT, state); } return 0; } static int audio_hw_get_mic_mute(const struct audio_hw_device *dev, bool *state) { struct tinyalsa_audio_device *device; LOGD("%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) { LOGD("%s(%p, %s)", __func__, dev, kvpairs); return -ENOSYS; } static char *audio_hw_get_parameters(const struct audio_hw_device *dev, const char *keys) { LOGD("%s(%p, %s)", __func__, dev, keys); return NULL; } static size_t audio_hw_get_input_buffer_size(const struct audio_hw_device *dev, uint32_t sample_rate, int format, int channel_count) { LOGD("%s(%p, %d, %d, %d)", __func__, dev, sample_rate, format, channel_count); return 320; } static int audio_hw_dump(const audio_hw_device_t *device, int fd) { LOGD("%s(%p, %d)", __func__, device, fd); return 0; } /* * Interface */ int audio_hw_close(hw_device_t *device) { struct tinyalsa_audio_device *tinyalsa_audio_device; LOGD("%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; LOGD("%s(%p, %s, %p)", __func__, module, name, device); 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 = 0; dev->common.module = (struct hw_module_t *) module; dev->common.close = audio_hw_close; dev->get_supported_devices = audio_hw_get_supported_devices; 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) { LOGE("Failed to open Yamaha-MC1N2-Audio"); goto error_device; } rc = yamaha_mc1n2_audio_init(tinyalsa_audio_device->mc1n2_pdata); if(rc < 0) { LOGE("Failed to init Yamaha-MC1N2-Audio"); } #endif rc = tinyalsa_mixer_open(&tinyalsa_mixer, TINYALSA_MIXER_CONFIG_FILE); if(rc < 0 || tinyalsa_mixer == NULL) { LOGE("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, .version_major = 1, .version_minor = 0, .id = AUDIO_HARDWARE_MODULE_ID, .name = "TinyALSA-Audio", .author = "Paul Kocialkowski", .methods = &audio_hw_module_methods, }, };