| /* |
| ** 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 |