mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:23:37 +00:00
Merge "audio: fix mmap output" into main
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#include <aidl/android/media/audio/common/AudioDeviceType.h>
|
||||
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
|
||||
#include <aidl/android/media/audio/common/AudioInputFlags.h>
|
||||
#include <aidl/android/media/audio/common/AudioIoFlags.h>
|
||||
#include <aidl/android/media/audio/common/AudioMode.h>
|
||||
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
|
||||
#include <aidl/android/media/audio/common/PcmType.h>
|
||||
@@ -188,4 +189,15 @@ constexpr int32_t frameCountFromDurationMs(int32_t durationMs, int32_t sampleRat
|
||||
return frameCountFromDurationUs(durationMs * 1000, sampleRateHz);
|
||||
}
|
||||
|
||||
constexpr bool hasMmapFlag(const ::aidl::android::media::audio::common::AudioIoFlags& flags) {
|
||||
return (flags.getTag() == ::aidl::android::media::audio::common::AudioIoFlags::Tag::input &&
|
||||
isBitPositionFlagSet(
|
||||
flags.get<::aidl::android::media::audio::common::AudioIoFlags::Tag::input>(),
|
||||
::aidl::android::media::audio::common::AudioInputFlags::MMAP_NOIRQ)) ||
|
||||
(flags.getTag() == ::aidl::android::media::audio::common::AudioIoFlags::Tag::output &&
|
||||
isBitPositionFlagSet(
|
||||
flags.get<::aidl::android::media::audio::common::AudioIoFlags::Tag::output>(),
|
||||
::aidl::android::media::audio::common::AudioOutputFlags::MMAP_NOIRQ));
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::common
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
using aidl::android::hardware::audio::common::frameCountFromDurationMs;
|
||||
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
|
||||
using aidl::android::hardware::audio::common::hasMmapFlag;
|
||||
using aidl::android::hardware::audio::common::isBitPositionFlagSet;
|
||||
using aidl::android::hardware::audio::common::isValidAudioMode;
|
||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||
@@ -205,35 +206,31 @@ ndk::ScopedAStatus Module::createStreamContext(
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const auto& flags = portConfigIt->flags.value();
|
||||
if ((flags.getTag() == AudioIoFlags::Tag::input &&
|
||||
!isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::input>(),
|
||||
AudioInputFlags::MMAP_NOIRQ)) ||
|
||||
(flags.getTag() == AudioIoFlags::Tag::output &&
|
||||
!isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::output>(),
|
||||
AudioOutputFlags::MMAP_NOIRQ))) {
|
||||
StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
|
||||
mVendorDebug.forceTransientBurst,
|
||||
mVendorDebug.forceSynchronousDrain};
|
||||
std::shared_ptr<ISoundDose> soundDose;
|
||||
if (!getSoundDose(&soundDose).isOk()) {
|
||||
LOG(ERROR) << __func__ << ": could not create sound dose instance";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
StreamContext temp(
|
||||
std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
|
||||
std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
|
||||
portConfigIt->format.value(), portConfigIt->channelMask.value(),
|
||||
portConfigIt->sampleRate.value().value, flags, nominalLatencyMs,
|
||||
portConfigIt->ext.get<AudioPortExt::mix>().handle,
|
||||
std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
|
||||
asyncCallback, outEventCallback, mSoundDose.getInstance(), params);
|
||||
if (temp.isValid()) {
|
||||
*out_context = std::move(temp);
|
||||
} else {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
|
||||
mVendorDebug.forceTransientBurst,
|
||||
mVendorDebug.forceSynchronousDrain};
|
||||
std::unique_ptr<StreamContext::DataMQ> dataMQ = nullptr;
|
||||
std::shared_ptr<IStreamCallback> streamAsyncCallback = nullptr;
|
||||
std::shared_ptr<ISoundDose> soundDose;
|
||||
if (!getSoundDose(&soundDose).isOk()) {
|
||||
LOG(ERROR) << __func__ << ": could not create sound dose instance";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
if (!hasMmapFlag(flags)) {
|
||||
dataMQ = std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames);
|
||||
streamAsyncCallback = asyncCallback;
|
||||
}
|
||||
StreamContext temp(
|
||||
std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
|
||||
std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
|
||||
portConfigIt->format.value(), portConfigIt->channelMask.value(),
|
||||
portConfigIt->sampleRate.value().value, flags, nominalLatencyMs,
|
||||
portConfigIt->ext.get<AudioPortExt::mix>().handle, std::move(dataMQ),
|
||||
streamAsyncCallback, outEventCallback, mSoundDose.getInstance(), params);
|
||||
if (temp.isValid()) {
|
||||
*out_context = std::move(temp);
|
||||
} else {
|
||||
// TODO: Implement simulation of MMAP buffer allocation
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
@@ -373,6 +370,13 @@ int32_t Module::getNominalLatencyMs(const AudioPortConfig&) {
|
||||
return kLatencyMs;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::createMmapBuffer(
|
||||
const ::aidl::android::hardware::audio::core::StreamContext& context __unused,
|
||||
::aidl::android::hardware::audio::core::StreamDescriptor* desc __unused) {
|
||||
LOG(ERROR) << __func__ << ": " << mType << ": is not implemented";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
std::vector<AudioRoute*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
|
||||
std::vector<AudioRoute*> result;
|
||||
auto& routes = getConfig().routes;
|
||||
@@ -866,6 +870,9 @@ ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_ar
|
||||
RETURN_STATUS_IF_ERROR(createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
|
||||
nullptr, nullptr, &context));
|
||||
context.fillDescriptor(&_aidl_return->desc);
|
||||
if (hasMmapFlag(context.getFlags())) {
|
||||
RETURN_STATUS_IF_ERROR(createMmapBuffer(context, &_aidl_return->desc));
|
||||
}
|
||||
std::shared_ptr<StreamIn> stream;
|
||||
RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata,
|
||||
getMicrophoneInfos(), &stream));
|
||||
@@ -913,6 +920,9 @@ ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_
|
||||
isNonBlocking ? in_args.callback : nullptr,
|
||||
in_args.eventCallback, &context));
|
||||
context.fillDescriptor(&_aidl_return->desc);
|
||||
if (hasMmapFlag(context.getFlags())) {
|
||||
RETURN_STATUS_IF_ERROR(createMmapBuffer(context, &_aidl_return->desc));
|
||||
}
|
||||
std::shared_ptr<StreamOut> stream;
|
||||
RETURN_STATUS_IF_ERROR(createOutputStream(std::move(context), in_args.sourceMetadata,
|
||||
in_args.offloadInfo, &stream));
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
using aidl::android::hardware::audio::common::AudioOffloadMetadata;
|
||||
using aidl::android::hardware::audio::common::getChannelCount;
|
||||
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
|
||||
using aidl::android::hardware::audio::common::hasMmapFlag;
|
||||
using aidl::android::hardware::audio::common::isBitPositionFlagSet;
|
||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using aidl::android::hardware::audio::common::SourceMetadata;
|
||||
@@ -84,7 +85,7 @@ bool StreamContext::isValid() const {
|
||||
LOG(ERROR) << "frame size is invalid";
|
||||
return false;
|
||||
}
|
||||
if (mDataMQ && !mDataMQ->isValid()) {
|
||||
if (!hasMmapFlag(mFlags) && mDataMQ && !mDataMQ->isValid()) {
|
||||
LOG(ERROR) << "data FMQ is invalid";
|
||||
return false;
|
||||
}
|
||||
@@ -116,17 +117,19 @@ pid_t StreamWorkerCommonLogic::getTid() const {
|
||||
std::string StreamWorkerCommonLogic::init() {
|
||||
if (mContext->getCommandMQ() == nullptr) return "Command MQ is null";
|
||||
if (mContext->getReplyMQ() == nullptr) return "Reply MQ is null";
|
||||
StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
|
||||
if (dataMQ == nullptr) return "Data MQ is null";
|
||||
if (sizeof(DataBufferElement) != dataMQ->getQuantumSize()) {
|
||||
return "Unexpected Data MQ quantum size: " + std::to_string(dataMQ->getQuantumSize());
|
||||
}
|
||||
mDataBufferSize = dataMQ->getQuantumCount() * dataMQ->getQuantumSize();
|
||||
mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
|
||||
if (mDataBuffer == nullptr) {
|
||||
return "Failed to allocate data buffer for element count " +
|
||||
std::to_string(dataMQ->getQuantumCount()) +
|
||||
", size in bytes: " + std::to_string(mDataBufferSize);
|
||||
if (!hasMmapFlag(mContext->getFlags())) {
|
||||
StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
|
||||
if (dataMQ == nullptr) return "Data MQ is null";
|
||||
if (sizeof(DataBufferElement) != dataMQ->getQuantumSize()) {
|
||||
return "Unexpected Data MQ quantum size: " + std::to_string(dataMQ->getQuantumSize());
|
||||
}
|
||||
mDataBufferSize = dataMQ->getQuantumCount() * dataMQ->getQuantumSize();
|
||||
mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
|
||||
if (mDataBuffer == nullptr) {
|
||||
return "Failed to allocate data buffer for element count " +
|
||||
std::to_string(dataMQ->getQuantumCount()) +
|
||||
", size in bytes: " + std::to_string(mDataBufferSize);
|
||||
}
|
||||
}
|
||||
if (::android::status_t status = mDriver->init(); status != STATUS_OK) {
|
||||
return "Failed to initialize the driver: " + std::to_string(status);
|
||||
@@ -136,16 +139,26 @@ std::string StreamWorkerCommonLogic::init() {
|
||||
|
||||
void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
|
||||
bool isConnected) const {
|
||||
static const StreamDescriptor::Position kUnknownPosition = {
|
||||
.frames = StreamDescriptor::Position::UNKNOWN,
|
||||
.timeNs = StreamDescriptor::Position::UNKNOWN};
|
||||
reply->status = STATUS_OK;
|
||||
if (isConnected) {
|
||||
reply->observable.frames = mContext->getFrameCount();
|
||||
reply->observable.timeNs = ::android::uptimeNanos();
|
||||
if (auto status = mDriver->refinePosition(&reply->observable); status == ::android::OK) {
|
||||
return;
|
||||
if (auto status = mDriver->refinePosition(&reply->observable); status != ::android::OK) {
|
||||
reply->observable = kUnknownPosition;
|
||||
}
|
||||
} else {
|
||||
reply->observable = reply->hardware = kUnknownPosition;
|
||||
}
|
||||
if (hasMmapFlag(mContext->getFlags())) {
|
||||
if (auto status = mDriver->getMmapPositionAndLatency(&reply->hardware, &reply->latencyMs);
|
||||
status != ::android::OK) {
|
||||
reply->hardware = kUnknownPosition;
|
||||
reply->latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
|
||||
}
|
||||
}
|
||||
reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
|
||||
reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
|
||||
}
|
||||
|
||||
void StreamWorkerCommonLogic::populateReplyWrongState(
|
||||
@@ -224,7 +237,9 @@ StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
|
||||
mState == StreamDescriptor::State::ACTIVE ||
|
||||
mState == StreamDescriptor::State::PAUSED ||
|
||||
mState == StreamDescriptor::State::DRAINING) {
|
||||
if (!read(fmqByteCount, &reply)) {
|
||||
if (hasMmapFlag(mContext->getFlags())) {
|
||||
populateReply(&reply, mIsConnected);
|
||||
} else if (!read(fmqByteCount, &reply)) {
|
||||
mState = StreamDescriptor::State::ERROR;
|
||||
}
|
||||
if (mState == StreamDescriptor::State::IDLE ||
|
||||
@@ -470,7 +485,9 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
|
||||
if (mState != StreamDescriptor::State::ERROR &&
|
||||
mState != StreamDescriptor::State::TRANSFERRING &&
|
||||
mState != StreamDescriptor::State::TRANSFER_PAUSED) {
|
||||
if (!write(fmqByteCount, &reply)) {
|
||||
if (hasMmapFlag(mContext->getFlags())) {
|
||||
populateReply(&reply, mIsConnected);
|
||||
} else if (!write(fmqByteCount, &reply)) {
|
||||
mState = StreamDescriptor::State::ERROR;
|
||||
}
|
||||
std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
|
||||
@@ -657,6 +674,7 @@ ndk::ScopedAStatus StreamCommonImpl::initInstance(
|
||||
const std::shared_ptr<StreamCommonInterface>& delegate) {
|
||||
mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
|
||||
if (!mWorker->start()) {
|
||||
LOG(ERROR) << __func__ << ": Worker start error: " << mWorker->getError();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
if (auto flags = getContext().getFlags();
|
||||
|
||||
@@ -205,6 +205,9 @@ class Module : public BnModule {
|
||||
virtual std::unique_ptr<Configuration> initializeConfig();
|
||||
virtual int32_t getNominalLatencyMs(
|
||||
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig);
|
||||
virtual ndk::ScopedAStatus createMmapBuffer(
|
||||
const ::aidl::android::hardware::audio::core::StreamContext& context,
|
||||
::aidl::android::hardware::audio::core::StreamDescriptor* desc);
|
||||
|
||||
// Utility and helper functions accessible to subclasses.
|
||||
static int32_t calculateBufferSizeFrames(int32_t latencyMs, int32_t sampleRateHz) {
|
||||
|
||||
@@ -177,6 +177,11 @@ struct DriverInterface {
|
||||
virtual ::android::status_t refinePosition(StreamDescriptor::Position* /*position*/) {
|
||||
return ::android::OK;
|
||||
}
|
||||
// Implement 'getMmapPositionAndLatency' is necessary if driver can support mmap stream.
|
||||
virtual ::android::status_t getMmapPositionAndLatency(StreamDescriptor::Position* /*position*/,
|
||||
int32_t* /*latency*/) {
|
||||
return ::android::OK;
|
||||
}
|
||||
virtual void shutdown() = 0; // This function is only called once.
|
||||
};
|
||||
|
||||
@@ -241,6 +246,7 @@ struct StreamWorkerInterface {
|
||||
virtual bool start() = 0;
|
||||
virtual pid_t getTid() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual std::string getError() = 0;
|
||||
};
|
||||
|
||||
template <class WorkerLogic>
|
||||
@@ -260,6 +266,7 @@ class StreamWorkerImpl : public StreamWorkerInterface,
|
||||
}
|
||||
pid_t getTid() override { return WorkerImpl::getTid(); }
|
||||
void stop() override { return WorkerImpl::stop(); }
|
||||
std::string getError() override { return WorkerImpl::getError(); }
|
||||
};
|
||||
|
||||
class StreamInWorkerLogic : public StreamWorkerCommonLogic {
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
using namespace android;
|
||||
using aidl::android::hardware::audio::common::AudioOffloadMetadata;
|
||||
using aidl::android::hardware::audio::common::getChannelCount;
|
||||
using aidl::android::hardware::audio::common::hasMmapFlag;
|
||||
using aidl::android::hardware::audio::common::isAnyBitPositionFlagSet;
|
||||
using aidl::android::hardware::audio::common::isBitPositionFlagSet;
|
||||
using aidl::android::hardware::audio::common::isTelephonyDeviceType;
|
||||
@@ -637,19 +638,39 @@ class StreamContext {
|
||||
mCommandMQ(new CommandMQ(descriptor.command)),
|
||||
mReplyMQ(new ReplyMQ(descriptor.reply)),
|
||||
mBufferSizeFrames(descriptor.bufferSizeFrames),
|
||||
mDataMQ(maybeCreateDataMQ(descriptor)) {}
|
||||
mDataMQ(maybeCreateDataMQ(descriptor)),
|
||||
mIsMmapped(isMmapped(descriptor)),
|
||||
mSharedMemoryFd(maybeGetMmapFd(descriptor)) {
|
||||
if (isMmapped()) {
|
||||
mSharedMemory = (int8_t*)mmap(nullptr, getBufferSizeBytes(), PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, mSharedMemoryFd, 0);
|
||||
if (mSharedMemory == MAP_FAILED) {
|
||||
PLOG(ERROR) << __func__ << ": mmap() failed.";
|
||||
mSharedMemory = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
~StreamContext() {
|
||||
if (mSharedMemory != nullptr) {
|
||||
munmap(mSharedMemory, getBufferSizeBytes());
|
||||
}
|
||||
}
|
||||
void checkIsValid() const {
|
||||
EXPECT_NE(0UL, mFrameSizeBytes);
|
||||
ASSERT_NE(nullptr, mCommandMQ);
|
||||
EXPECT_TRUE(mCommandMQ->isValid());
|
||||
ASSERT_NE(nullptr, mReplyMQ);
|
||||
EXPECT_TRUE(mReplyMQ->isValid());
|
||||
if (mDataMQ != nullptr) {
|
||||
EXPECT_TRUE(mDataMQ->isValid());
|
||||
EXPECT_GE(mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize(),
|
||||
mFrameSizeBytes * mBufferSizeFrames)
|
||||
<< "Data MQ actual buffer size is "
|
||||
"less than the buffer size as specified by the descriptor";
|
||||
if (isMmapped()) {
|
||||
ASSERT_NE(nullptr, mSharedMemory);
|
||||
} else {
|
||||
if (mDataMQ != nullptr) {
|
||||
EXPECT_TRUE(mDataMQ->isValid());
|
||||
EXPECT_GE(mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize(),
|
||||
mFrameSizeBytes * mBufferSizeFrames)
|
||||
<< "Data MQ actual buffer size is "
|
||||
"less than the buffer size as specified by the descriptor";
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t getBufferSizeBytes() const { return mFrameSizeBytes * mBufferSizeFrames; }
|
||||
@@ -658,6 +679,8 @@ class StreamContext {
|
||||
DataMQ* getDataMQ() const { return mDataMQ.get(); }
|
||||
size_t getFrameSizeBytes() const { return mFrameSizeBytes; }
|
||||
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
|
||||
bool isMmapped() const { return mIsMmapped; }
|
||||
int8_t* getMmapMemory() const { return mSharedMemory; }
|
||||
|
||||
private:
|
||||
static std::unique_ptr<DataMQ> maybeCreateDataMQ(const StreamDescriptor& descriptor) {
|
||||
@@ -667,12 +690,26 @@ class StreamContext {
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static bool isMmapped(const StreamDescriptor& descriptor) {
|
||||
using Tag = StreamDescriptor::AudioBuffer::Tag;
|
||||
return descriptor.audio.getTag() == Tag::mmap;
|
||||
}
|
||||
static int32_t maybeGetMmapFd(const StreamDescriptor& descriptor) {
|
||||
using Tag = StreamDescriptor::AudioBuffer::Tag;
|
||||
if (descriptor.audio.getTag() == Tag::mmap) {
|
||||
return descriptor.audio.get<Tag::mmap>().sharedMemory.fd.get();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size_t mFrameSizeBytes;
|
||||
std::unique_ptr<CommandMQ> mCommandMQ;
|
||||
std::unique_ptr<ReplyMQ> mReplyMQ;
|
||||
const size_t mBufferSizeFrames;
|
||||
std::unique_ptr<DataMQ> mDataMQ;
|
||||
const bool mIsMmapped;
|
||||
const int32_t mSharedMemoryFd;
|
||||
int8_t* mSharedMemory = nullptr;
|
||||
};
|
||||
|
||||
struct StreamEventReceiver {
|
||||
@@ -868,12 +905,15 @@ class StreamCommonLogic : public StreamLogic {
|
||||
mDataMQ(context.getDataMQ()),
|
||||
mData(context.getBufferSizeBytes()),
|
||||
mDriver(driver),
|
||||
mEventReceiver(eventReceiver) {}
|
||||
mEventReceiver(eventReceiver),
|
||||
mIsMmapped(context.isMmapped()),
|
||||
mSharedMemory(context.getMmapMemory()) {}
|
||||
StreamContext::CommandMQ* getCommandMQ() const { return mCommandMQ; }
|
||||
StreamContext::ReplyMQ* getReplyMQ() const { return mReplyMQ; }
|
||||
StreamContext::DataMQ* getDataMQ() const { return mDataMQ; }
|
||||
StreamLogicDriver* getDriver() const { return mDriver; }
|
||||
StreamEventReceiver* getEventReceiver() const { return mEventReceiver; }
|
||||
bool isMmapped() const { return mIsMmapped; }
|
||||
|
||||
std::string init() override {
|
||||
LOG(DEBUG) << __func__;
|
||||
@@ -914,6 +954,22 @@ class StreamCommonLogic : public StreamLogic {
|
||||
LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to MQ failed";
|
||||
return false;
|
||||
}
|
||||
bool readDataFromMmap(size_t readCount) {
|
||||
if (mSharedMemory != nullptr) {
|
||||
std::memcpy(mData.data(), mSharedMemory, readCount);
|
||||
return true;
|
||||
}
|
||||
LOG(ERROR) << __func__ << ": reading of " << readCount << " bytes from mmap failed";
|
||||
return false;
|
||||
}
|
||||
bool writeDataToMmap() {
|
||||
if (mSharedMemory != nullptr) {
|
||||
std::memcpy(mSharedMemory, mData.data(), mData.size());
|
||||
return true;
|
||||
}
|
||||
LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to mmap failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
StreamContext::CommandMQ* mCommandMQ;
|
||||
@@ -923,6 +979,8 @@ class StreamCommonLogic : public StreamLogic {
|
||||
StreamLogicDriver* const mDriver;
|
||||
StreamEventReceiver* const mEventReceiver;
|
||||
int mLastEventSeq = StreamEventReceiver::kEventSeqInit;
|
||||
const bool mIsMmapped;
|
||||
int8_t* mSharedMemory = nullptr;
|
||||
};
|
||||
|
||||
class StreamReaderLogic : public StreamCommonLogic {
|
||||
@@ -970,7 +1028,8 @@ class StreamReaderLogic : public StreamCommonLogic {
|
||||
<< ": received invalid byte count in the reply: " << reply.fmqByteCount;
|
||||
return Status::ABORT;
|
||||
}
|
||||
if (static_cast<size_t>(reply.fmqByteCount) != getDataMQ()->availableToRead()) {
|
||||
if (!isMmapped() &&
|
||||
static_cast<size_t>(reply.fmqByteCount) != getDataMQ()->availableToRead()) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": the byte count in the reply is not the same as the amount of "
|
||||
<< "data available in the MQ: " << reply.fmqByteCount
|
||||
@@ -991,8 +1050,10 @@ class StreamReaderLogic : public StreamCommonLogic {
|
||||
return Status::ABORT;
|
||||
}
|
||||
const bool acceptedReply = getDriver()->processValidReply(reply);
|
||||
if (const size_t readCount = getDataMQ()->availableToRead(); readCount > 0) {
|
||||
if (readDataFromMQ(readCount)) {
|
||||
if (const size_t readCount =
|
||||
!isMmapped() ? getDataMQ()->availableToRead() : reply.fmqByteCount;
|
||||
readCount > 0) {
|
||||
if (isMmapped() ? readDataFromMmap(readCount) : readDataFromMQ(readCount)) {
|
||||
goto checkAcceptedReply;
|
||||
}
|
||||
LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
|
||||
@@ -1028,8 +1089,10 @@ class StreamWriterLogic : public StreamCommonLogic {
|
||||
LOG(ERROR) << __func__ << ": no next command";
|
||||
return Status::ABORT;
|
||||
}
|
||||
if (actualSize != 0 && !writeDataToMQ()) {
|
||||
return Status::ABORT;
|
||||
if (actualSize != 0) {
|
||||
if (isMmapped() ? !writeDataToMmap() : !writeDataToMQ()) {
|
||||
return Status::ABORT;
|
||||
}
|
||||
}
|
||||
LOG(DEBUG) << "Writing command: " << command.toString();
|
||||
if (!getCommandMQ()->writeBlocking(&command, 1)) {
|
||||
@@ -1058,7 +1121,7 @@ class StreamWriterLogic : public StreamCommonLogic {
|
||||
return Status::ABORT;
|
||||
}
|
||||
// It is OK for the implementation to leave data in the MQ when the stream is paused.
|
||||
if (reply.state != StreamDescriptor::State::PAUSED &&
|
||||
if (!isMmapped() && reply.state != StreamDescriptor::State::PAUSED &&
|
||||
getDataMQ()->availableToWrite() != getDataMQ()->getQuantumCount()) {
|
||||
LOG(ERROR) << __func__ << ": the HAL module did not consume all data from the data MQ: "
|
||||
<< "available to write " << getDataMQ()->availableToWrite()
|
||||
@@ -2904,15 +2967,24 @@ class StreamFixture {
|
||||
|
||||
class StreamLogicDefaultDriver : public StreamLogicDriver {
|
||||
public:
|
||||
StreamLogicDefaultDriver(std::shared_ptr<StateSequence> commands, size_t frameSizeBytes)
|
||||
: mCommands(commands), mFrameSizeBytes(frameSizeBytes) {
|
||||
StreamLogicDefaultDriver(std::shared_ptr<StateSequence> commands, size_t frameSizeBytes,
|
||||
bool isMmap)
|
||||
: mCommands(commands), mFrameSizeBytes(frameSizeBytes), mIsMmap(isMmap) {
|
||||
mCommands->rewind();
|
||||
}
|
||||
|
||||
// The three methods below is intended to be called after the worker
|
||||
// The five methods below is intended to be called after the worker
|
||||
// thread has joined, thus no extra synchronization is needed.
|
||||
bool hasObservablePositionIncrease() const { return mObservablePositionIncrease; }
|
||||
bool hasRetrogradeObservablePosition() const { return mRetrogradeObservablePosition; }
|
||||
bool hasObservableRetrogradePosition() const { return mRetrogradeObservablePosition; }
|
||||
bool hasHardwarePositionIncrease() const {
|
||||
// For non-MMap, always return true to pass the validation.
|
||||
return mIsMmap ? mHardwarePositionIncrease : true;
|
||||
}
|
||||
bool hasHardwareRetrogradePosition() const {
|
||||
// For non-MMap, always return false to pass the validation.
|
||||
return mIsMmap ? mRetrogradeHardwarePosition : false;
|
||||
}
|
||||
std::string getUnexpectedStateTransition() const { return mUnexpectedTransition; }
|
||||
|
||||
bool done() override { return mCommands->done(); }
|
||||
@@ -2940,14 +3012,24 @@ class StreamLogicDefaultDriver : public StreamLogicDriver {
|
||||
bool interceptRawReply(const StreamDescriptor::Reply&) override { return false; }
|
||||
bool processValidReply(const StreamDescriptor::Reply& reply) override {
|
||||
if (reply.observable.frames != StreamDescriptor::Position::UNKNOWN) {
|
||||
if (mPreviousFrames.has_value()) {
|
||||
if (reply.observable.frames > mPreviousFrames.value()) {
|
||||
if (mPreviousObservableFrames.has_value()) {
|
||||
if (reply.observable.frames > mPreviousObservableFrames.value()) {
|
||||
mObservablePositionIncrease = true;
|
||||
} else if (reply.observable.frames < mPreviousFrames.value()) {
|
||||
} else if (reply.observable.frames < mPreviousObservableFrames.value()) {
|
||||
mRetrogradeObservablePosition = true;
|
||||
}
|
||||
}
|
||||
mPreviousFrames = reply.observable.frames;
|
||||
mPreviousObservableFrames = reply.observable.frames;
|
||||
}
|
||||
if (mIsMmap) {
|
||||
if (mPreviousHardwareFrames.has_value()) {
|
||||
if (reply.hardware.frames > mPreviousHardwareFrames.value()) {
|
||||
mHardwarePositionIncrease = true;
|
||||
} else if (reply.hardware.frames < mPreviousHardwareFrames.value()) {
|
||||
mRetrogradeHardwarePosition = true;
|
||||
}
|
||||
}
|
||||
mPreviousHardwareFrames = reply.hardware.frames;
|
||||
}
|
||||
|
||||
auto expected = mCommands->getExpectedStates();
|
||||
@@ -2974,10 +3056,14 @@ class StreamLogicDefaultDriver : public StreamLogicDriver {
|
||||
protected:
|
||||
std::shared_ptr<StateSequence> mCommands;
|
||||
const size_t mFrameSizeBytes;
|
||||
const bool mIsMmap;
|
||||
std::optional<StreamDescriptor::State> mPreviousState;
|
||||
std::optional<int64_t> mPreviousFrames;
|
||||
std::optional<int64_t> mPreviousObservableFrames;
|
||||
bool mObservablePositionIncrease = false;
|
||||
bool mRetrogradeObservablePosition = false;
|
||||
std::optional<int64_t> mPreviousHardwareFrames;
|
||||
bool mHardwarePositionIncrease = false;
|
||||
bool mRetrogradeHardwarePosition = false;
|
||||
std::string mUnexpectedTransition;
|
||||
};
|
||||
|
||||
@@ -2988,8 +3074,8 @@ std::shared_ptr<StateSequence> makeBurstCommands(bool isSync);
|
||||
static bool skipStreamIoTestForMixPortConfig(const AudioPortConfig& portConfig) {
|
||||
return (portConfig.flags.value().getTag() == AudioIoFlags::input &&
|
||||
isAnyBitPositionFlagSet(portConfig.flags.value().template get<AudioIoFlags::input>(),
|
||||
{AudioInputFlags::MMAP_NOIRQ, AudioInputFlags::VOIP_TX,
|
||||
AudioInputFlags::HW_HOTWORD, AudioInputFlags::HOTWORD_TAP})) ||
|
||||
{AudioInputFlags::VOIP_TX, AudioInputFlags::HW_HOTWORD,
|
||||
AudioInputFlags::HOTWORD_TAP})) ||
|
||||
(portConfig.flags.value().getTag() == AudioIoFlags::output &&
|
||||
isAnyBitPositionFlagSet(
|
||||
portConfig.flags.value().template get<AudioIoFlags::output>(),
|
||||
@@ -3029,8 +3115,8 @@ class StreamFixtureWithWorker {
|
||||
|
||||
void StartWorkerToSendBurstCommands() {
|
||||
const StreamContext* context = mStream->getStreamContext();
|
||||
mWorkerDriver = std::make_unique<StreamLogicDefaultDriver>(makeBurstCommands(mIsSync),
|
||||
context->getFrameSizeBytes());
|
||||
mWorkerDriver = std::make_unique<StreamLogicDefaultDriver>(
|
||||
makeBurstCommands(mIsSync), context->getFrameSizeBytes(), context->isMmapped());
|
||||
mWorker = std::make_unique<typename IOTraits<Stream>::Worker>(
|
||||
*context, mWorkerDriver.get(), mStream->getStreamEventReceiver());
|
||||
LOG(DEBUG) << __func__ << ": starting " << IOTraits<Stream>::directionStr << " worker...";
|
||||
@@ -3047,10 +3133,13 @@ class StreamFixtureWithWorker {
|
||||
EXPECT_FALSE(mWorker->hasError()) << mWorker->getError();
|
||||
EXPECT_EQ("", mWorkerDriver->getUnexpectedStateTransition());
|
||||
if (validatePosition) {
|
||||
if (IOTraits<Stream>::is_input) {
|
||||
if (IOTraits<Stream>::is_input &&
|
||||
!mStream->getStreamContext()->isMmapped() /*TODO(b/274456992) remove*/) {
|
||||
EXPECT_TRUE(mWorkerDriver->hasObservablePositionIncrease());
|
||||
EXPECT_TRUE(mWorkerDriver->hasHardwarePositionIncrease());
|
||||
}
|
||||
EXPECT_FALSE(mWorkerDriver->hasRetrogradeObservablePosition());
|
||||
EXPECT_FALSE(mWorkerDriver->hasObservableRetrogradePosition());
|
||||
EXPECT_FALSE(mWorkerDriver->hasHardwareRetrogradePosition());
|
||||
}
|
||||
mWorker.reset();
|
||||
mWorkerDriver.reset();
|
||||
@@ -3984,7 +4073,7 @@ class AudioStreamIo : public AudioCoreModuleBase,
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidateObservablePosition(const AudioDevice& device) {
|
||||
bool ValidatePosition(const AudioDevice& device) {
|
||||
return !isTelephonyDeviceType(device.type.type);
|
||||
}
|
||||
|
||||
@@ -3998,7 +4087,8 @@ class AudioStreamIo : public AudioCoreModuleBase,
|
||||
if (skipStreamIoTestForDevice(stream.getDevice())) return;
|
||||
ASSERT_EQ("", stream.skipTestReason());
|
||||
StreamLogicDefaultDriver driver(commandsAndStates,
|
||||
stream.getStreamContext()->getFrameSizeBytes());
|
||||
stream.getStreamContext()->getFrameSizeBytes(),
|
||||
stream.getStreamContext()->isMmapped());
|
||||
typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
|
||||
stream.getStreamEventReceiver());
|
||||
|
||||
@@ -4008,11 +4098,14 @@ class AudioStreamIo : public AudioCoreModuleBase,
|
||||
worker.join();
|
||||
EXPECT_FALSE(worker.hasError()) << worker.getError();
|
||||
EXPECT_EQ("", driver.getUnexpectedStateTransition());
|
||||
if (ValidateObservablePosition(stream.getDevice())) {
|
||||
if (validatePositionIncrease) {
|
||||
if (ValidatePosition(stream.getDevice())) {
|
||||
if (validatePositionIncrease &&
|
||||
!stream.getStreamContext()->isMmapped() /*TODO(b/274456992) remove*/) {
|
||||
EXPECT_TRUE(driver.hasObservablePositionIncrease());
|
||||
EXPECT_TRUE(driver.hasHardwarePositionIncrease());
|
||||
}
|
||||
EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
|
||||
EXPECT_FALSE(driver.hasObservableRetrogradePosition());
|
||||
EXPECT_FALSE(driver.hasHardwareRetrogradePosition());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4028,7 +4121,8 @@ class AudioStreamIo : public AudioCoreModuleBase,
|
||||
ASSERT_EQ("", stream.skipTestReason());
|
||||
ASSERT_NO_FATAL_FAILURE(stream.TeardownPatchSetUpStream(module.get()));
|
||||
StreamLogicDefaultDriver driver(commandsAndStates,
|
||||
stream.getStreamContext()->getFrameSizeBytes());
|
||||
stream.getStreamContext()->getFrameSizeBytes(),
|
||||
stream.getStreamContext()->isMmapped());
|
||||
typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
|
||||
stream.getStreamEventReceiver());
|
||||
ASSERT_NO_FATAL_FAILURE(stream.ReconnectPatch(module.get()));
|
||||
@@ -4039,11 +4133,14 @@ class AudioStreamIo : public AudioCoreModuleBase,
|
||||
worker.join();
|
||||
EXPECT_FALSE(worker.hasError()) << worker.getError();
|
||||
EXPECT_EQ("", driver.getUnexpectedStateTransition());
|
||||
if (ValidateObservablePosition(stream.getDevice())) {
|
||||
if (validatePositionIncrease) {
|
||||
if (ValidatePosition(stream.getDevice())) {
|
||||
if (validatePositionIncrease &&
|
||||
!stream.getStreamContext()->isMmapped() /*TODO(b/274456992) remove*/) {
|
||||
EXPECT_TRUE(driver.hasObservablePositionIncrease());
|
||||
EXPECT_TRUE(driver.hasHardwarePositionIncrease());
|
||||
}
|
||||
EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
|
||||
EXPECT_FALSE(driver.hasObservableRetrogradePosition());
|
||||
EXPECT_FALSE(driver.hasHardwareRetrogradePosition());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user