/* * Copyright (C) 2012 Paul Kocialkowski * * 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 RIL Interface" #include #include #include #include #include #include #include #include #define EFFECT_UUID_NULL EFFECT_UUID_NULL_RIL #define EFFECT_UUID_NULL_STR EFFECT_UUID_NULL_STR_RIL #include "audio_hw.h" #include "audio_ril_interface.h" /* Function pointers */ void *(*_ril_open_client)(void); int (*_ril_close_client)(void *); int (*_ril_connect)(void *); int (*_ril_is_connected)(void *); int (*_ril_disconnect)(void *); int (*_ril_set_call_volume)(void *, enum ril_sound_type, int); int (*_ril_set_call_audio_path)(void *, enum ril_audio_path); int (*_ril_set_call_clock_sync)(void *, enum ril_clock_state); int (*_ril_set_call_twomic)(void *, enum ril_twomic_device, enum ril_twomic_enable); int (*_ril_set_mic_mute)(void *, enum ril_mic_mute); int (*_ril_register_unsolicited_handler)(void *, int, void *); int (*_ril_get_wb_amr)(void *, void *); #define VOLUME_STEPS_DEFAULT "5" #define VOLUME_STEPS_PROPERTY "ro.config.vc_call_vol_steps" static int audio_ril_interface_connect_if_required(struct tinyalsa_audio_ril_interface *ril_interface) { if (_ril_is_connected(ril_interface->interface)) return 0; if (_ril_connect(ril_interface->interface) != RIL_CLIENT_ERR_SUCCESS) { ALOGE("ril_connect() failed"); return -1; } /* get wb amr status to set pcm samplerate depending on wb amr status when ril is connected. */ /* FIXME: AMR */ #if 0 if(_ril_get_wb_amr) _ril_get_wb_amr(ril_interface->client, ril_set_wb_amr_callback); #endif return 0; } int audio_ril_interface_set_voice_volume(struct tinyalsa_audio_ril_interface *ril_interface, audio_devices_t device, float volume) { int rc; enum ril_sound_type sound_type; if(ril_interface == NULL) return -1; ALOGD("%s(%d, %f)", __func__, device, volume); pthread_mutex_lock(&ril_interface->lock); /* Should this be returning -1 when a failure occurs? */ if (audio_ril_interface_connect_if_required(ril_interface)) return 0; if(_ril_set_call_volume == NULL) goto error; switch((int) device) { case AUDIO_DEVICE_OUT_EARPIECE: sound_type = SOUND_TYPE_VOICE; break; case AUDIO_DEVICE_OUT_SPEAKER: sound_type = SOUND_TYPE_SPEAKER; break; case AUDIO_DEVICE_OUT_WIRED_HEADSET: case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: sound_type = SOUND_TYPE_HEADSET; break; case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: sound_type = SOUND_TYPE_BTVOICE; break; default: sound_type = SOUND_TYPE_VOICE; break; } rc = _ril_set_call_volume(ril_interface->interface, sound_type, (int)(volume * ril_interface->volume_steps_max)); if(rc < 0) { ALOGE("Failed to set RIL interface voice volume"); goto error; } pthread_mutex_unlock(&ril_interface->lock); return 0; error: pthread_mutex_unlock(&ril_interface->lock); return -1; } int audio_ril_interface_set_route(struct tinyalsa_audio_ril_interface *ril_interface, audio_devices_t device) { int rc; enum ril_audio_path path; ALOGD("%s(%d)", __func__, device); if(ril_interface == NULL) return -1; pthread_mutex_lock(&ril_interface->lock); /* Should this be returning -1 when a failure occurs? */ if (audio_ril_interface_connect_if_required(ril_interface)) return 0; ril_interface->device_current = device; if(_ril_set_call_audio_path == NULL) goto error; switch((int) device) { case AUDIO_DEVICE_OUT_EARPIECE: path = SOUND_AUDIO_PATH_HANDSET; break; case AUDIO_DEVICE_OUT_SPEAKER: path = SOUND_AUDIO_PATH_SPEAKER; break; case AUDIO_DEVICE_OUT_WIRED_HEADSET: path = SOUND_AUDIO_PATH_HEADSET; break; case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: path = SOUND_AUDIO_PATH_HEADPHONE; break; // FIXME: Bluetooth values/path relation case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: path = SOUND_AUDIO_PATH_BLUETOOTH; case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: path = SOUND_AUDIO_PATH_BLUETOOTH_NO_NR; break; default: path = SOUND_AUDIO_PATH_HANDSET; break; } rc = _ril_set_call_audio_path(ril_interface->interface,path); if(rc < 0) { ALOGE("Failed to set RIL interface route"); goto error; } pthread_mutex_unlock(&ril_interface->lock); return 0; error: pthread_mutex_unlock(&ril_interface->lock); return -1; } int audio_ril_interface_set_mic_mute(struct tinyalsa_audio_ril_interface *ril_interface, enum ril_mic_mute state) { if (audio_ril_interface_connect_if_required(ril_interface)) return 0; return _ril_set_mic_mute(ril_interface->interface, state); } int audio_ril_interface_set_twomic(struct tinyalsa_audio_ril_interface *ril_interface, enum ril_twomic_enable twomic) { int rc; ALOGD("%s(%d)", __func__, twomic); if(ril_interface == NULL) return -1; pthread_mutex_lock(&ril_interface->lock); /* Should this be returning -1 when a failure occurs? */ if (audio_ril_interface_connect_if_required(ril_interface)) return 0; if(_ril_set_call_twomic == NULL) goto error; rc = _ril_set_call_twomic(ril_interface->interface,AUDIENCE,twomic); if(rc < 0) { ALOGE("Failed to set RIL interface route"); goto error; } pthread_mutex_unlock(&ril_interface->lock); return 0; error: pthread_mutex_unlock(&ril_interface->lock); return -1; } /* * Interface */ void audio_ril_interface_close(struct audio_hw_device *dev, struct tinyalsa_audio_ril_interface *ril_interface) { struct tinyalsa_audio_device *tinyalsa_audio_device; ALOGD("%s(%p)", __func__, ril_interface); if(ril_interface->dl_handle != NULL) { if ((_ril_disconnect(ril_interface->interface) != RIL_CLIENT_ERR_SUCCESS) || (_ril_close_client(ril_interface->interface) != RIL_CLIENT_ERR_SUCCESS)) { ALOGE("ril_disconnect() or ril_close_client() failed"); return; } dlclose(ril_interface->dl_handle); ril_interface->dl_handle = NULL; } if(ril_interface != NULL) free(ril_interface); if(dev == NULL) return; tinyalsa_audio_device = (struct tinyalsa_audio_device *) dev; tinyalsa_audio_device->ril_interface = NULL; } int audio_ril_interface_open(struct audio_hw_device *dev, audio_devices_t device, struct tinyalsa_audio_ril_interface **ril_interface) { struct audio_ril_interface *(*audio_ril_interface_open)(void); struct tinyalsa_audio_device *tinyalsa_audio_device; struct tinyalsa_audio_ril_interface *tinyalsa_audio_ril_interface; struct audio_ril_interface *interface; void *dl_handle; int rc; char property[PROPERTY_VALUE_MAX]; ALOGD("%s(%p, %d, %p)", __func__, dev, device, ril_interface); if(dev == NULL || ril_interface == NULL) return -EINVAL; tinyalsa_audio_device = (struct tinyalsa_audio_device *) dev; tinyalsa_audio_ril_interface = calloc(1, sizeof(struct tinyalsa_audio_ril_interface)); if(tinyalsa_audio_ril_interface == NULL) return -ENOMEM; tinyalsa_audio_ril_interface->device = tinyalsa_audio_device; tinyalsa_audio_device->ril_interface = tinyalsa_audio_ril_interface; dl_handle = dlopen(RIL_CLIENT_LIBPATH, RTLD_NOW); if(dl_handle == NULL) { ALOGE("Unable to dlopen lib: %s", RIL_CLIENT_LIBPATH); goto error_interface; } _ril_open_client = dlsym(dl_handle, "OpenClient_RILD"); _ril_close_client = dlsym(dl_handle, "CloseClient_RILD"); _ril_connect = dlsym(dl_handle, "Connect_RILD"); _ril_is_connected = dlsym(dl_handle, "isConnected_RILD"); _ril_disconnect = dlsym(dl_handle, "Disconnect_RILD"); _ril_set_call_volume = dlsym(dl_handle, "SetCallVolume"); _ril_set_call_audio_path = dlsym(dl_handle, "SetCallAudioPath"); _ril_set_call_clock_sync = dlsym(dl_handle, "SetCallClockSync"); _ril_set_call_twomic = dlsym(dl_handle, "SetTwoMicControl"); _ril_set_mic_mute = dlsym(dl_handle, "SetMute"); _ril_register_unsolicited_handler = dlsym(dl_handle, "RegisterUnsolicitedHandler"); /* since this function is not supported in all RILs, don't require it */ _ril_get_wb_amr = dlsym(dl_handle, "GetWB_AMR"); if (!_ril_open_client || !_ril_close_client || !_ril_connect || !_ril_is_connected || !_ril_disconnect || !_ril_set_call_volume || !_ril_set_call_audio_path || !_ril_set_mic_mute || !_ril_set_call_clock_sync || !_ril_register_unsolicited_handler || !_ril_set_call_twomic) { ALOGE("Cannot get symbols from '%s'", RIL_CLIENT_LIBPATH); dlclose(dl_handle); goto error_interface; } interface = _ril_open_client(); if(interface == NULL) { ALOGE("Unable to open audio ril interface"); goto error_interface; } tinyalsa_audio_ril_interface->interface = interface; tinyalsa_audio_ril_interface->dl_handle = dl_handle; property_get(VOLUME_STEPS_PROPERTY, property, VOLUME_STEPS_DEFAULT); tinyalsa_audio_ril_interface->volume_steps_max = atoi(property); /* this catches the case where VOLUME_STEPS_PROPERTY does not contain an integer */ if (tinyalsa_audio_ril_interface->volume_steps_max == 0) tinyalsa_audio_ril_interface->volume_steps_max = atoi(VOLUME_STEPS_DEFAULT); if(device) { tinyalsa_audio_ril_interface->device_current = device; audio_ril_interface_set_route(tinyalsa_audio_ril_interface, device); } *ril_interface = tinyalsa_audio_ril_interface; return 0; error_interface: *ril_interface = NULL; free(tinyalsa_audio_ril_interface); tinyalsa_audio_device->ril_interface = NULL; if(dl_handle != NULL) dlclose(dl_handle); return -1; }