/*
** Copyright 2008, 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.
*/
#include <math.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioHardwareMSM72XX"
#include <utils/Log.h>
#include <utils/String8.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <fcntl.h>
// hardware specific functions
#include "AudioHardware.h"
#include <media/AudioRecord.h>
#define LOG_SND_RPC 0 // Set to 1 to log sound RPC's
namespace android {
static int audpre_index, tx_iir_index;
static void * acoustic;
const uint32_t AudioHardware::inputSamplingRates[] = {
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
};
// ----------------------------------------------------------------------------
AudioHardware::AudioHardware() :
mInit(false), mMicMute(true), mBluetoothNrec(true), mBluetoothId(0),
mOutput(0), mSndEndpoints(NULL), mCurSndDevice(-1),
SND_DEVICE_CURRENT(-1),
SND_DEVICE_HANDSET(-1),
SND_DEVICE_SPEAKER(-1),
SND_DEVICE_HEADSET(-1),
SND_DEVICE_BT(-1),
SND_DEVICE_CARKIT(-1),
SND_DEVICE_TTY_FULL(-1),
SND_DEVICE_TTY_VCO(-1),
SND_DEVICE_TTY_HCO(-1),
SND_DEVICE_NO_MIC_HEADSET(-1),
SND_DEVICE_FM_HEADSET(-1),
SND_DEVICE_HEADSET_AND_SPEAKER(-1),
SND_DEVICE_FM_SPEAKER(-1),
SND_DEVICE_BT_EC_OFF(-1)
{
int (*snd_get_num)();
int (*snd_get_endpoint)(int, msm_snd_endpoint *);
int (*set_acoustic_parameters)();
struct msm_snd_endpoint *ept;
acoustic = ::dlopen("/system/lib/libhtc_acoustic.so", RTLD_NOW);
if (acoustic == NULL ) {
ALOGE("Could not open libhtc_acoustic.so");
/* this is not really an error on non-htc devices... */
mNumSndEndpoints = 0;
mInit = true;
return;
}
set_acoustic_parameters = (int (*)(void))::dlsym(acoustic, "set_acoustic_parameters");
if ((*set_acoustic_parameters) == 0 ) {
ALOGE("Could not open set_acoustic_parameters()");
return;
}
int rc = set_acoustic_parameters();
if (rc < 0) {
ALOGE("Could not set acoustic parameters to share memory: %d", rc);
// return;
}
snd_get_num = (int (*)(void))::dlsym(acoustic, "snd_get_num_endpoints");
if ((*snd_get_num) == 0 ) {
ALOGE("Could not open snd_get_num()");
// return;
}
mNumSndEndpoints = snd_get_num();
ALOGD("mNumSndEndpoints = %d", mNumSndEndpoints);
mSndEndpoints = new msm_snd_endpoint[mNumSndEndpoints];
mInit = true;
ALOGV("constructed %d SND endpoints)", mNumSndEndpoints);
ept = mSndEndpoints;
snd_get_endpoint = (int (*)(int, msm_snd_endpoint *))::dlsym(acoustic, "snd_get_endpoint");
if ((*snd_get_endpoint) == 0 ) {
ALOGE("Could not open snd_get_endpoint()");
return;
}
for (int cnt = 0; cnt < mNumSndEndpoints; cnt++, ept++) {
ept->id = cnt;
snd_get_endpoint(cnt, ept);
#define CHECK_FOR(desc) \
if (!strcmp(ept->name, #desc)) { \
SND_DEVICE_##desc = ept->id; \
ALOGD("BT MATCH " #desc); \
} else
CHECK_FOR(CURRENT)
CHECK_FOR(HANDSET)
CHECK_FOR(SPEAKER)
CHECK_FOR(BT)
CHECK_FOR(BT_EC_OFF)
CHECK_FOR(HEADSET)
CHECK_FOR(CARKIT)
CHECK_FOR(TTY_FULL)
CHECK_FOR(TTY_VCO)
CHECK_FOR(TTY_HCO)
CHECK_FOR(NO_MIC_HEADSET)
CHECK_FOR(FM_HEADSET)
CHECK_FOR(FM_SPEAKER)
CHECK_FOR(HEADSET_AND_SPEAKER) {}
#undef CHECK_FOR
}
}
AudioHardware::~AudioHardware()
{
for (size_t index = 0; index < mInputs.size(); index++) {
closeInputStream((AudioStreamIn*)mInputs[index]);
}
mInputs.clear();
closeOutputStream((AudioStreamOut*)mOutput);
delete [] mSndEndpoints;
if (acoustic) {
::dlclose(acoustic);
acoustic = 0;
}
mInit = false;
}
status_t AudioHardware::initCheck()
{
return mInit ? NO_ERROR : NO_INIT;
}
AudioStreamOut* AudioHardware::openOutputStream(
uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
{
{ // scope for the lock
Mutex::Autolock lock(mLock);
// only one output stream allowed
if (mOutput) {
if (status) {
*status = INVALID_OPERATION;
}
return 0;
}
// create new output stream
AudioStreamOutMSM72xx* out = new AudioStreamOutMSM72xx();
status_t lStatus = out->set(this, devices, format, channels, sampleRate);
if (status) {
*status = lStatus;
}
if (lStatus == NO_ERROR) {
mOutput = out;
} else {
delete out;
}
}
return mOutput;
}
void AudioHardware::closeOutputStream(AudioStreamOut* out) {
Mutex::Autolock lock(mLock);
if (mOutput == 0 || mOutput != out) {
ALOGW("Attempt to close invalid output stream");
}
else {
delete mOutput;
mOutput = 0;
}
}
AudioStreamIn* AudioHardware::openInputStream(
uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
AudioSystem::audio_in_acoustics acoustic_flags)
{
// check for valid input source
if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
return 0;
}
mLock.lock();
AudioStreamInMSM72xx* in = new AudioStreamInMSM72xx();
status_t lStatus = in->set(this, devices, format, channels, sampleRate, acoustic_flags);
if (status) {
*status = lStatus;
}
if (lStatus != NO_ERROR) {
mLock.unlock();
delete in;
return 0;
}
mInputs.add(in);
mLock.unlock();
return in;
}
void AudioHardware::closeInputStream(AudioStreamIn* in) {
Mutex::Autolock lock(mLock);
ssize_t index = mInputs.indexOf((AudioStreamInMSM72xx *)in);
if (index < 0) {
ALOGW("Attempt to close invalid input stream");
} else {
mLock.unlock();
delete mInputs[index];
mLock.lock();
mInputs.removeAt(index);
}
}
status_t AudioHardware::setMode(int mode)
{
status_t status = AudioHardwareBase::setMode(mode);
if (status == NO_ERROR) {
// make sure that doAudioRouteOrMute() is called by doRouting()
// even if the new device selected is the same as current one.
clearCurDevice();
}
return status;
}
bool AudioHardware::checkOutputStandby()
{
if (mOutput)
if (!mOutput->checkStandby())
return false;
return true;
}
status_t AudioHardware::setMicMute(bool state)
{
Mutex::Autolock lock(mLock);
return setMicMute_nosync(state);
}
// always call with mutex held
status_t AudioHardware::setMicMute_nosync(bool state)
{
if (mMicMute != state) {
mMicMute = state;
return doAudioRouteOrMute(SND_DEVICE_CURRENT);
}
return NO_ERROR;
}
status_t AudioHardware::getMicMute(bool* state)
{
*state = mMicMute;
return NO_ERROR;
}
status_t AudioHardware::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 value;
String8 key;
const char BT_NREC_KEY[] = "bt_headset_nrec";
const char BT_NAME_KEY[] = "bt_headset_name";
const char BT_NREC_VALUE_ON[] = "on";
ALOGV("setParameters() %s", keyValuePairs.string());
if (keyValuePairs.length() == 0) return BAD_VALUE;
key = String8(BT_NREC_KEY);
if (param.get(key, value) == NO_ERROR) {
if (value == BT_NREC_VALUE_ON) {
mBluetoothNrec = true;
} else {
mBluetoothNrec = false;
ALOGI("Turning noise reduction and echo cancellation off for BT "
"headset");
}
}
key = String8(BT_NAME_KEY);
if (param.get(key, value) == NO_ERROR) {
mBluetoothId = 0;
for (int i = 0; i < mNumSndEndpoints; i++) {
if (!strcasecmp(value.string(), mSndEndpoints[i].name)) {
mBluetoothId = mSndEndpoints[i].id;
ALOGI("Using custom acoustic parameters for %s", value.string());
break;
}
}
if (mBluetoothId == 0) {
ALOGI("Using default acoustic parameters "
"(%s not in acoustic database)", value.string());
doRouting();
}
}
return NO_ERROR;
}
String8 AudioHardware::getParameters(const String8& keys)
{
AudioParameter param = AudioParameter(keys);
return param.toString();
}
static unsigned calculate_audpre_table_index(unsigned index)
{
switch (index) {
case 48000: return SAMP_RATE_INDX_48000;
case 44100: return SAMP_RATE_INDX_44100;
case 32000: return SAMP_RATE_INDX_32000;
case 24000: return SAMP_RATE_INDX_24000;
case 22050: return SAMP_RATE_INDX_22050;
case 16000: return SAMP_RATE_INDX_16000;
case 12000: return SAMP_RATE_INDX_12000;
case 11025: return SAMP_RATE_INDX_11025;
case 8000: return SAMP_RATE_INDX_8000;
default: return -1;
}
}
size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
if (format != AudioSystem::PCM_16_BIT) {
ALOGW("getInputBufferSize bad format: %d", format);
return 0;
}
if (channelCount < 1 || channelCount > 2) {
ALOGW("getInputBufferSize bad channel count: %d", channelCount);
return 0;
}
return 2048*channelCount;
}
static status_t set_volume_rpc(uint32_t device,
uint32_t method,
uint32_t volume)
{
int fd;
#if LOG_SND_RPC
ALOGD("rpc_snd_set_volume(%d, %d, %d)\n", device, method, volume);
#endif
if (device == -1UL) return NO_ERROR;
fd = open("/dev/msm_snd", O_RDWR);
if (fd < 0) {
ALOGE("Can not open snd device");
return -EPERM;
}
/* rpc_snd_set_volume(
* device, # Any hardware device enum, including
* # SND_DEVICE_CURRENT
* method, # must be SND_METHOD_VOICE to do anything useful
* volume, # integer volume level, in range [0,5].
* # note that 0 is audible (not quite muted)
* )
* rpc_snd_set_volume only works for in-call sound volume.
*/
struct msm_snd_volume_config args;
args.device = device;
args.method = method;
args.volume = volume;
if (ioctl(fd, SND_SET_VOLUME, &args) < 0) {
ALOGE("snd_set_volume error.");
close(fd);
return -EIO;
}
close(fd);
return NO_ERROR;
}
status_t AudioHardware::setVoiceVolume(float v)
{
if (v < 0.0) {
ALOGW("setVoiceVolume(%f) under 0.0, assuming 0.0\n", v);
v = 0.0;
} else if (v > 1.0) {
ALOGW("setVoiceVolume(%f) over 1.0, assuming 1.0\n", v);
v = 1.0;
}
int vol = lrint(v * 5.0);
ALOGD("setVoiceVolume(%f)\n", v);
ALOGI("Setting in-call volume to %d (available range is 0 to 5)\n", vol);
Mutex::Autolock lock(mLock);
set_volume_rpc(SND_DEVICE_CURRENT, SND_METHOD_VOICE, vol);
return NO_ERROR;
}
status_t AudioHardware::setMasterVolume(float v)
{
Mutex::Autolock lock(mLock);
int vol = ceil(v * 5.0);
ALOGI("Set master volume to %d.\n", vol);
/*
set_volume_rpc(SND_DEVICE_HANDSET, SND_METHOD_VOICE, vol);
set_volume_rpc(SND_DEVICE_SPEAKER, SND_METHOD_VOICE, vol);
set_volume_rpc(SND_DEVICE_BT, SND_METHOD_VOICE, vol);
set_volume_rpc(SND_DEVICE_HEADSET, SND_METHOD_VOICE, vol);
*/
// We return an error code here to let the audioflinger do in-software
// volume on top of the maximum volume that we set through the SND API.
// return error - software mixer will handle it
return -1;
}
static status_t do_route_audio_rpc(uint32_t device,
bool ear_mute, bool mic_mute)
{
if (device == -1UL)
return NO_ERROR;
int fd;
#if LOG_SND_RPC
ALOGD("rpc_snd_set_device(%d, %d, %d)\n", device, ear_mute, mic_mute);
#endif
fd = open("/dev/msm_snd", O_RDWR);
if (fd < 0) {
ALOGE("Can not open snd device");
return -EPERM;
}
// RPC call to switch audio path
/* rpc_snd_set_device(
* device, # Hardware device enum to use
* ear_mute, # Set mute for outgoing voice audio
* # this should only be unmuted when in-call
* mic_mute, # Set mute for incoming voice audio
* # this should only be unmuted when in-call or
* # recording.
* )
*/
struct msm_snd_device_config args;
args.device = device;
args.ear_mute = ear_mute ? SND_MUTE_MUTED : SND_MUTE_UNMUTED;
args.mic_mute = mic_mute ? SND_MUTE_MUTED : SND_MUTE_UNMUTED;
if (ioctl(fd, SND_SET_DEVICE, &args) < 0) {
ALOGE("snd_set_device error.");
close(fd);
return -EIO;
}
close(fd);
return NO_ERROR;
}
// always call with mutex held
status_t AudioHardware::doAudioRouteOrMute(uint32_t device)
{
if (device == (uint32_t)SND_DEVICE_BT || device == (uint32_t)SND_DEVICE_CARKIT) {
if (mBluetoothId) {
device = mBluetoothId;
} else if (!mBluetoothNrec) {
device = SND_DEVICE_BT_EC_OFF;
}
}
ALOGV("doAudioRouteOrMute() device %x, mMode %d, mMicMute %d", device, mMode, mMicMute);
return do_route_audio_rpc(device,
mMode != AudioSystem::MODE_IN_CALL, mMicMute);
}
status_t AudioHardware::doRouting()
{
/* currently this code doesn't work without the htc libacoustic */
if (!acoustic)
return 0;
Mutex::Autolock lock(mLock);
uint32_t outputDevices = mOutput->devices();
status_t ret = NO_ERROR;
int (*msm72xx_enable_audpp)(int);
msm72xx_enable_audpp = (int (*)(int))::dlsym(acoustic, "msm72xx_enable_audpp");
int audProcess = (ADRC_DISABLE | EQ_DISABLE | RX_IIR_DISABLE);
AudioStreamInMSM72xx *input = getActiveInput_l();
uint32_t inputDevice = (input == NULL) ? 0 : input->devices();
int sndDevice = -1;
if (inputDevice != 0) {
ALOGI("do input routing device %x\n", inputDevice);
if (inputDevice & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
ALOGI("Routing audio to Bluetooth PCM\n");
sndDevice = SND_DEVICE_BT;
} else if (inputDevice & AudioSystem::DEVICE_IN_WIRED_HEADSET) {
if ((outputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) &&
(outputDevices & AudioSystem::DEVICE_OUT_SPEAKER)) {
ALOGI("Routing audio to Wired Headset and Speaker\n");
sndDevice = SND_DEVICE_HEADSET_AND_SPEAKER;
audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE);
} else {
ALOGI("Routing audio to Wired Headset\n");
sndDevice = SND_DEVICE_HEADSET;
}
} else {
if (outputDevices & AudioSystem::DEVICE_OUT_SPEAKER) {
ALOGI("Routing audio to Speakerphone\n");
sndDevice = SND_DEVICE_SPEAKER;
audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE);
} else {
ALOGI("Routing audio to Handset\n");
sndDevice = SND_DEVICE_HANDSET;
}
}
}
// if inputDevice == 0, restore output routing
if (sndDevice == -1) {
if (outputDevices & (outputDevices - 1)) {
if ((outputDevices & AudioSystem::DEVICE_OUT_SPEAKER) == 0) {
ALOGW("Hardware does not support requested route combination (%#X),"
" picking closest possible route...", outputDevices);
}
}
if (outputDevices &
(AudioSystem::DEVICE_OUT_BLUETOOTH_SCO | AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET)) {
ALOGI("Routing audio to Bluetooth PCM\n");
sndDevice = SND_DEVICE_BT;
} else if (outputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
ALOGI("Routing audio to Bluetooth PCM\n");
sndDevice = SND_DEVICE_CARKIT;
} else if ((outputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) &&
(outputDevices & AudioSystem::DEVICE_OUT_SPEAKER)) {
ALOGI("Routing audio to Wired Headset and Speaker\n");
sndDevice = SND_DEVICE_HEADSET_AND_SPEAKER;
audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE);
} else if (outputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) {
if (outputDevices & AudioSystem::DEVICE_OUT_SPEAKER) {
ALOGI("Routing audio to No microphone Wired Headset and Speaker (%d,%x)\n", mMode, outputDevices);
sndDevice = SND_DEVICE_HEADSET_AND_SPEAKER;
audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE);
} else {
ALOGI("Routing audio to No microphone Wired Headset (%d,%x)\n", mMode, outputDevices);
sndDevice = SND_DEVICE_NO_MIC_HEADSET;
}
} else if (outputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
ALOGI("Routing audio to Wired Headset\n");
sndDevice = SND_DEVICE_HEADSET;
} else if (outputDevices & AudioSystem::DEVICE_OUT_SPEAKER) {
ALOGI("Routing audio to Speakerphone\n");
sndDevice = SND_DEVICE_SPEAKER;
audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE);
} else {
ALOGI("Routing audio to Handset\n");
sndDevice = SND_DEVICE_HANDSET;
}
}
if (sndDevice != -1 && sndDevice != mCurSndDevice) {
ret = doAudioRouteOrMute(sndDevice);
if ((*msm72xx_enable_audpp) == 0 ) {
ALOGE("Could not open msm72xx_enable_audpp()");
} else {
msm72xx_enable_audpp(audProcess);
}
mCurSndDevice = sndDevice;
}
return ret;
}
status_t AudioHardware::checkMicMute()
{
Mutex::Autolock lock(mLock);
if (mMode != AudioSystem::MODE_IN_CALL) {
setMicMute_nosync(true);
}
return NO_ERROR;
}
status_t AudioHardware::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
result.append("AudioHardware::dumpInternals\n");
snprintf(buffer, SIZE, "\tmInit: %s\n", mInit? "true": "false");
result.append(buffer);
snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false");
result.append(buffer);
snprintf(buffer, SIZE, "\tmBluetoothNrec: %s\n", mBluetoothNrec? "true": "false");
result.append(buffer);
snprintf(buffer, SIZE, "\tmBluetoothId: %d\n", mBluetoothId);
result.append(buffer);
::write(fd, result.string(), result.size());
return NO_ERROR;
}
status_t AudioHardware::dump(int fd, const Vector<String16>& args)
{
dumpInternals(fd, args);
for (size_t index = 0; index < mInputs.size(); index++) {
mInputs[index]->dump(fd, args);
}
if (mOutput) {
mOutput->dump(fd, args);
}
return NO_ERROR;
}
uint32_t AudioHardware::getInputSampleRate(uint32_t sampleRate)
{
uint32_t i;
uint32_t prevDelta;
uint32_t delta;
for (i = 0, prevDelta = 0xFFFFFFFF; i < sizeof(inputSamplingRates)/sizeof(uint32_t); i++, prevDelta = delta) {
delta = abs(sampleRate - inputSamplingRates[i]);
if (delta > prevDelta) break;
}
// i is always > 0 here
return inputSamplingRates[i-1];
}
// getActiveInput_l() must be called with mLock held
AudioHardware::AudioStreamInMSM72xx *AudioHardware::getActiveInput_l()
{
for (size_t i = 0; i < mInputs.size(); i++) {
// return first input found not being in standby mode
// as only one input can be in this state
if (mInputs[i]->state() > AudioStreamInMSM72xx::AUDIO_INPUT_CLOSED) {
return mInputs[i];
}
}
return NULL;
}
// ----------------------------------------------------------------------------
AudioHardware::AudioStreamOutMSM72xx::AudioStreamOutMSM72xx() :
mHardware(0), mFd(-1), mStartCount(0), mRetryCount(0), mStandby(true), mDevices(0)
{
}
status_t AudioHardware::AudioStreamOutMSM72xx::set(
AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
{
int lFormat = pFormat ? *pFormat : 0;
uint32_t lChannels = pChannels ? *pChannels : 0;
uint32_t lRate = pRate ? *pRate : 0;
mHardware = hw;
// fix up defaults
if (lFormat == 0) lFormat = format();
if (lChannels == 0) lChannels = channels();
if (lRate == 0) lRate = sampleRate();
// check values
if ((lFormat != format()) ||
(lChannels != channels()) ||
(lRate != sampleRate())) {
if (pFormat) *pFormat = format();
if (pChannels) *pChannels = channels();
if (pRate) *pRate = sampleRate();
return BAD_VALUE;
}
if (pFormat) *pFormat = lFormat;
if (pChannels) *pChannels = lChannels;
if (pRate) *pRate = lRate;
mDevices = devices;
return NO_ERROR;
}
AudioHardware::AudioStreamOutMSM72xx::~AudioStreamOutMSM72xx()
{
if (mFd >= 0) close(mFd);
}
ssize_t AudioHardware::AudioStreamOutMSM72xx::write(const void* buffer, size_t bytes)
{
// ALOGD("AudioStreamOutMSM72xx::write(%p, %u)", buffer, bytes);
status_t status = NO_INIT;
size_t count = bytes;
const uint8_t* p = static_cast<const uint8_t*>(buffer);
if (mStandby) {
// open driver
ALOGV("open driver");
status = ::open("/dev/msm_pcm_out", O_RDWR);
if (status < 0) {
ALOGE("Cannot open /dev/msm_pcm_out errno: %d", errno);
goto Error;
}
mFd = status;
// configuration
ALOGV("get config");
struct msm_audio_config config;
status = ioctl(mFd, AUDIO_GET_CONFIG, &config);
if (status < 0) {
ALOGE("Cannot read config");
goto Error;
}
ALOGV("set config");
config.channel_count = AudioSystem::popCount(channels());
config.sample_rate = sampleRate();
config.buffer_size = bufferSize();
config.buffer_count = AUDIO_HW_NUM_OUT_BUF;
config.codec_type = CODEC_TYPE_PCM;
status = ioctl(mFd, AUDIO_SET_CONFIG, &config);
if (status < 0) {
ALOGE("Cannot set config");
goto Error;
}
ALOGV("buffer_size: %u", config.buffer_size);
ALOGV("buffer_count: %u", config.buffer_count);
ALOGV("channel_count: %u", config.channel_count);
ALOGV("sample_rate: %u", config.sample_rate);
// fill 2 buffers before AUDIO_START
mStartCount = AUDIO_HW_NUM_OUT_BUF;
mStandby = false;
}
while (count) {
ssize_t written = ::write(mFd, p, count);
if (written >= 0) {
count -= written;
p += written;
} else {
if (errno != EAGAIN) return written;
mRetryCount++;
ALOGW("EAGAIN - retry");
}
}
// start audio after we fill 2 buffers
if (mStartCount) {
if (--mStartCount == 0) {
ioctl(mFd, AUDIO_START, 0);
}
}
return bytes;
Error:
if (mFd >= 0) {
::close(mFd);
mFd = -1;
}
// Simulate audio output timing in case of error
usleep(bytes * 1000000 / frameSize() / sampleRate());
return status;
}
status_t AudioHardware::AudioStreamOutMSM72xx::standby()
{
status_t status = NO_ERROR;
if (!mStandby && mFd >= 0) {
::close(mFd);
mFd = -1;
}
mStandby = true;
return status;
}
status_t AudioHardware::AudioStreamOutMSM72xx::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
result.append("AudioStreamOutMSM72xx::dump\n");
snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
result.append(buffer);
snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
result.append(buffer);
snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
result.append(buffer);
snprintf(buffer, SIZE, "\tformat: %d\n", format());
result.append(buffer);
snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware);
result.append(buffer);
snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
result.append(buffer);
snprintf(buffer, SIZE, "\tmStartCount: %d\n", mStartCount);
result.append(buffer);
snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount);
result.append(buffer);
snprintf(buffer, SIZE, "\tmStandby: %s\n", mStandby? "true": "false");
result.append(buffer);
::write(fd, result.string(), result.size());
return NO_ERROR;
}
bool AudioHardware::AudioStreamOutMSM72xx::checkStandby()
{
return mStandby;
}
status_t AudioHardware::AudioStreamOutMSM72xx::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 key = String8(AudioParameter::keyRouting);
status_t status = NO_ERROR;
int device;
ALOGV("AudioStreamOutMSM72xx::setParameters() %s", keyValuePairs.string());
if (param.getInt(key, device) == NO_ERROR) {
mDevices = device;
ALOGV("set output routing %x", mDevices);
status = mHardware->doRouting();
param.remove(key);
}
if (param.size()) {
status = BAD_VALUE;
}
return status;
}
String8 AudioHardware::AudioStreamOutMSM72xx::getParameters(const String8& keys)
{
AudioParameter param = AudioParameter(keys);
String8 value;
String8 key = String8(AudioParameter::keyRouting);
if (param.get(key, value) == NO_ERROR) {
ALOGV("get routing %x", mDevices);
param.addInt(key, (int)mDevices);
}
ALOGV("AudioStreamOutMSM72xx::getParameters() %s", param.toString().string());
return param.toString();
}
status_t AudioHardware::AudioStreamOutMSM72xx::getRenderPosition(uint32_t *dspFrames)
{
//TODO: enable when supported by driver
return INVALID_OPERATION;
}
// ----------------------------------------------------------------------------
AudioHardware::AudioStreamInMSM72xx::AudioStreamInMSM72xx() :
mHardware(0), mFd(-1), mState(AUDIO_INPUT_CLOSED), mRetryCount(0),
mFormat(AUDIO_HW_IN_FORMAT), mChannels(AUDIO_HW_IN_CHANNELS),
mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_BUFFERSIZE),
mAcoustics((AudioSystem::audio_in_acoustics)0), mDevices(0)
{
}
status_t AudioHardware::AudioStreamInMSM72xx::set(
AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate,
AudioSystem::audio_in_acoustics acoustic_flags)
{
if (pFormat == 0 || *pFormat != AUDIO_HW_IN_FORMAT) {
*pFormat = AUDIO_HW_IN_FORMAT;
return BAD_VALUE;
}
if (pRate == 0) {
return BAD_VALUE;
}
uint32_t rate = hw->getInputSampleRate(*pRate);
if (rate != *pRate) {
*pRate = rate;
return BAD_VALUE;
}
if (pChannels == 0 || (*pChannels != AudioSystem::CHANNEL_IN_MONO &&
*pChannels != AudioSystem::CHANNEL_IN_STEREO)) {
*pChannels = AUDIO_HW_IN_CHANNELS;
return BAD_VALUE;
}
mHardware = hw;
ALOGV("AudioStreamInMSM72xx::set(%d, %d, %u)", *pFormat, *pChannels, *pRate);
if (mFd >= 0) {
ALOGE("Audio record already open");
return -EPERM;
}
// open audio input device
status_t status = ::open("/dev/msm_pcm_in", O_RDWR);
if (status < 0) {
ALOGE("Cannot open /dev/msm_pcm_in errno: %d", errno);
goto Error;
}
mFd = status;
// configuration
ALOGV("get config");
struct msm_audio_config config;
status = ioctl(mFd, AUDIO_GET_CONFIG, &config);
if (status < 0) {
ALOGE("Cannot read config");
goto Error;
}
ALOGV("set config");
config.channel_count = AudioSystem::popCount(*pChannels);
config.sample_rate = *pRate;
config.buffer_size = bufferSize();
config.buffer_count = 2;
config.codec_type = CODEC_TYPE_PCM;
status = ioctl(mFd, AUDIO_SET_CONFIG, &config);
if (status < 0) {
ALOGE("Cannot set config");
if (ioctl(mFd, AUDIO_GET_CONFIG, &config) == 0) {
if (config.channel_count == 1) {
*pChannels = AudioSystem::CHANNEL_IN_MONO;
} else {
*pChannels = AudioSystem::CHANNEL_IN_STEREO;
}
*pRate = config.sample_rate;
}
goto Error;
}
ALOGV("confirm config");
status = ioctl(mFd, AUDIO_GET_CONFIG, &config);
if (status < 0) {
ALOGE("Cannot read config");
goto Error;
}
ALOGV("buffer_size: %u", config.buffer_size);
ALOGV("buffer_count: %u", config.buffer_count);
ALOGV("channel_count: %u", config.channel_count);
ALOGV("sample_rate: %u", config.sample_rate);
mDevices = devices;
mFormat = AUDIO_HW_IN_FORMAT;
mChannels = *pChannels;
mSampleRate = config.sample_rate;
mBufferSize = config.buffer_size;
//mHardware->setMicMute_nosync(false);
mState = AUDIO_INPUT_OPENED;
if (!acoustic)
return NO_ERROR;
audpre_index = calculate_audpre_table_index(mSampleRate);
tx_iir_index = (audpre_index * 2) + (hw->checkOutputStandby() ? 0 : 1);
ALOGD("audpre_index = %d, tx_iir_index = %d\n", audpre_index, tx_iir_index);
/**
* If audio-preprocessing failed, we should not block record.
*/
int (*msm72xx_set_audpre_params)(int, int);
msm72xx_set_audpre_params = (int (*)(int, int))::dlsym(acoustic, "msm72xx_set_audpre_params");
status = msm72xx_set_audpre_params(audpre_index, tx_iir_index);
if (status < 0)
ALOGE("Cannot set audpre parameters");
int (*msm72xx_enable_audpre)(int, int, int);
msm72xx_enable_audpre = (int (*)(int, int, int))::dlsym(acoustic, "msm72xx_enable_audpre");
mAcoustics = acoustic_flags;
status = msm72xx_enable_audpre((int)acoustic_flags, audpre_index, tx_iir_index);
if (status < 0)
ALOGE("Cannot enable audpre");
return NO_ERROR;
Error:
if (mFd >= 0) {
::close(mFd);
mFd = -1;
}
return status;
}
AudioHardware::AudioStreamInMSM72xx::~AudioStreamInMSM72xx()
{
ALOGV("AudioStreamInMSM72xx destructor");
standby();
}
ssize_t AudioHardware::AudioStreamInMSM72xx::read( void* buffer, ssize_t bytes)
{
ALOGV("AudioStreamInMSM72xx::read(%p, %ld)", buffer, bytes);
if (!mHardware) return -1;
size_t count = bytes;
uint8_t* p = static_cast<uint8_t*>(buffer);
if (mState < AUDIO_INPUT_OPENED) {
Mutex::Autolock lock(mHardware->mLock);
if (set(mHardware, mDevices, &mFormat, &mChannels, &mSampleRate, mAcoustics) != NO_ERROR) {
return -1;
}
}
if (mState < AUDIO_INPUT_STARTED) {
mState = AUDIO_INPUT_STARTED;
// force routing to input device
mHardware->clearCurDevice();
mHardware->doRouting();
if (ioctl(mFd, AUDIO_START, 0)) {
ALOGE("Error starting record");
standby();
return -1;
}
}
while (count) {
ssize_t bytesRead = ::read(mFd, buffer, count);
if (bytesRead >= 0) {
count -= bytesRead;
p += bytesRead;
} else {
if (errno != EAGAIN) return bytesRead;
mRetryCount++;
ALOGW("EAGAIN - retrying");
}
}
return bytes;
}
status_t AudioHardware::AudioStreamInMSM72xx::standby()
{
if (mState > AUDIO_INPUT_CLOSED) {
if (mFd >= 0) {
::close(mFd);
mFd = -1;
}
mState = AUDIO_INPUT_CLOSED;
}
if (!mHardware) return -1;
// restore output routing if necessary
mHardware->clearCurDevice();
mHardware->doRouting();
return NO_ERROR;
}
status_t AudioHardware::AudioStreamInMSM72xx::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
result.append("AudioStreamInMSM72xx::dump\n");
snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
result.append(buffer);
snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
result.append(buffer);
snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
result.append(buffer);
snprintf(buffer, SIZE, "\tformat: %d\n", format());
result.append(buffer);
snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware);
result.append(buffer);
snprintf(buffer, SIZE, "\tmFd count: %d\n", mFd);
result.append(buffer);
snprintf(buffer, SIZE, "\tmState: %d\n", mState);
result.append(buffer);
snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount);
result.append(buffer);
::write(fd, result.string(), result.size());
return NO_ERROR;
}
status_t AudioHardware::AudioStreamInMSM72xx::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 key = String8(AudioParameter::keyRouting);
status_t status = NO_ERROR;
int device;
ALOGV("AudioStreamInMSM72xx::setParameters() %s", keyValuePairs.string());
if (param.getInt(key, device) == NO_ERROR) {
ALOGV("set input routing %x", device);
if (device & (device - 1)) {
status = BAD_VALUE;
} else {
mDevices = device;
status = mHardware->doRouting();
}
param.remove(key);
}
if (param.size()) {
status = BAD_VALUE;
}
return status;
}
String8 AudioHardware::AudioStreamInMSM72xx::getParameters(const String8& keys)
{
AudioParameter param = AudioParameter(keys);
String8 value;
String8 key = String8(AudioParameter::keyRouting);
if (param.get(key, value) == NO_ERROR) {
ALOGV("get routing %x", mDevices);
param.addInt(key, (int)mDevices);
}
ALOGV("AudioStreamInMSM72xx::getParameters() %s", param.toString().string());
return param.toString();
}
// ----------------------------------------------------------------------------
extern "C" AudioHardwareInterface* createAudioHardware(void) {
return new AudioHardware();
}
}; // namespace android