/*
** 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 "AudioHardware"
#include <utils/Log.h>
#include <utils/String8.h>
//#include <hardware_legacy/power.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>
#include "AudioHardware.h"
#include <media/AudioRecord.h>
extern "C" {
#include "msm_audio.h"
}
namespace android {
// ----------------------------------------------------------------------------
AudioHardware::AudioHardware() :
mInit(false), mMicMute(true), mOutput(0)
{
mInit = true;
}
AudioHardware::~AudioHardware()
{
closeOutputStream((AudioStreamOut*)mOutput);
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;
}
AudioStreamOutQ5V2* out = new AudioStreamOutQ5V2();
status_t rc = out->set(this, devices, format, channels, sampleRate);
if (rc) {
*status = rc;
}
if (rc == 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)
{
return 0;
}
void AudioHardware::closeInputStream(AudioStreamIn* in) {
}
status_t AudioHardware::setMode(int mode)
{
return NO_ERROR;
}
status_t AudioHardware::setMicMute(bool state)
{
return NO_ERROR;
}
status_t AudioHardware::getMicMute(bool* state)
{
*state = mMicMute;
return NO_ERROR;
}
status_t AudioHardware::setParameters(const String8& keyValuePairs)
{
return NO_ERROR;
}
String8 AudioHardware::getParameters(const String8& keys)
{
AudioParameter request = AudioParameter(keys);
AudioParameter reply = AudioParameter();
ALOGV("getParameters() %s", keys.string());
return reply.toString();
}
size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
return 4096;
}
status_t AudioHardware::setVoiceVolume(float v)
{
return NO_ERROR;
}
status_t AudioHardware::setMasterVolume(float v)
{
ALOGI("Set master volume to %f.\n", v);
// 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;
}
status_t AudioHardware::dump(int fd, const Vector<String16>& args)
{
return NO_ERROR;
}
AudioHardware::AudioStreamOutQ5V2::AudioStreamOutQ5V2() :
mHardware(0), mFd(-1), mStartCount(0), mRetryCount(0), mStandby(true),
mDevices(0), mChannels(AUDIO_HW_OUT_CHANNELS), mSampleRate(AUDIO_HW_OUT_SAMPLERATE),
mBufferSize(AUDIO_HW_OUT_BUFSZ)
{
}
status_t AudioHardware::AudioStreamOutQ5V2::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;
mDevices = devices;
// 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;
mChannels = lChannels;
mSampleRate = lRate;
mBufferSize = 4096;
return NO_ERROR;
}
AudioHardware::AudioStreamOutQ5V2::~AudioStreamOutQ5V2()
{
standby();
}
ssize_t AudioHardware::AudioStreamOutQ5V2::write(const void* buffer, size_t bytes)
{
// ALOGD("AudioStreamOutQ5V2::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) {
ALOGV("open pcm_out 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 pcm_out config");
goto Error;
}
ALOGV("set pcm_out config");
config.channel_count = AudioSystem::popCount(channels());
config.sample_rate = mSampleRate;
config.buffer_size = mBufferSize;
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);
#if 0
status = ioctl(mFd, AUDIO_START, &acdb_id);
if (status < 0) {
ALOGE("Cannot start pcm playback");
goto Error;
}
status = ioctl(mFd, AUDIO_SET_VOLUME, &stream_volume);
if (status < 0) {
ALOGE("Cannot start pcm playback");
goto Error;
}
#endif
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");
}
}
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::AudioStreamOutQ5V2::standby()
{
status_t status = NO_ERROR;
if (!mStandby && mFd >= 0) {
::close(mFd);
mFd = -1;
}
mStandby = true;
ALOGI("AudioHardware pcm playback is going to standby.");
return status;
}
status_t AudioHardware::AudioStreamOutQ5V2::dump(int fd, const Vector<String16>& args)
{
return NO_ERROR;
}
bool AudioHardware::AudioStreamOutQ5V2::checkStandby()
{
return mStandby;
}
status_t AudioHardware::AudioStreamOutQ5V2::setParameters(const String8& keyValuePairs)
{
return NO_ERROR;
}
String8 AudioHardware::AudioStreamOutQ5V2::getParameters(const String8& keys)
{
AudioParameter param = AudioParameter(keys);
ALOGV("AudioStreamOutQ5V2::getParameters() %s", param.toString().string());
return param.toString();
}
status_t AudioHardware::AudioStreamOutQ5V2::getRenderPosition(uint32_t *dspFrames)
{
return INVALID_OPERATION;
}
extern "C" AudioHardwareInterface* createAudioHardware(void) {
return new AudioHardware();
}
}; // namespace android