Merge "AOSP AIDL USB audio HAL implementation."
This commit is contained in:
commit
9275a96448
15 changed files with 937 additions and 10 deletions
|
@ -104,6 +104,30 @@ constexpr bool isTelephonyDeviceType(
|
|||
device == ::aidl::android::media::audio::common::AudioDeviceType::OUT_TELEPHONY_TX;
|
||||
}
|
||||
|
||||
constexpr bool isUsbInputDeviceType(::aidl::android::media::audio::common::AudioDeviceType type) {
|
||||
switch (type) {
|
||||
case ::aidl::android::media::audio::common::AudioDeviceType::IN_DOCK:
|
||||
case ::aidl::android::media::audio::common::AudioDeviceType::IN_ACCESSORY:
|
||||
case ::aidl::android::media::audio::common::AudioDeviceType::IN_DEVICE:
|
||||
case ::aidl::android::media::audio::common::AudioDeviceType::IN_HEADSET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool isUsbOutputtDeviceType(::aidl::android::media::audio::common::AudioDeviceType type) {
|
||||
switch (type) {
|
||||
case ::aidl::android::media::audio::common::AudioDeviceType::OUT_DOCK:
|
||||
case ::aidl::android::media::audio::common::AudioDeviceType::OUT_ACCESSORY:
|
||||
case ::aidl::android::media::audio::common::AudioDeviceType::OUT_DEVICE:
|
||||
case ::aidl::android::media::audio::common::AudioDeviceType::OUT_HEADSET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool isValidAudioMode(::aidl::android::media::audio::common::AudioMode mode) {
|
||||
return std::find(kValidAudioModes.begin(), kValidAudioModes.end(), mode) !=
|
||||
kValidAudioModes.end();
|
||||
|
|
|
@ -11,12 +11,14 @@ cc_defaults {
|
|||
name: "aidlaudioservice_defaults",
|
||||
vendor: true,
|
||||
shared_libs: [
|
||||
"libalsautilsv2",
|
||||
"libaudioaidlcommon",
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
"libstagefright_foundation",
|
||||
"libtinyalsav2",
|
||||
"libutils",
|
||||
"libxml2",
|
||||
"android.hardware.common-V2-ndk",
|
||||
|
@ -71,6 +73,9 @@ cc_library_static {
|
|||
"Stream.cpp",
|
||||
"StreamStub.cpp",
|
||||
"Telephony.cpp",
|
||||
"usb/ModuleUsb.cpp",
|
||||
"usb/StreamUsb.cpp",
|
||||
"usb/UsbAlsaUtils.cpp",
|
||||
],
|
||||
generated_sources: [
|
||||
"audio_policy_configuration_aidl_default",
|
||||
|
|
|
@ -27,8 +27,10 @@
|
|||
|
||||
#include "core-impl/Bluetooth.h"
|
||||
#include "core-impl/Module.h"
|
||||
#include "core-impl/ModuleUsb.h"
|
||||
#include "core-impl/SoundDose.h"
|
||||
#include "core-impl/StreamStub.h"
|
||||
#include "core-impl/StreamUsb.h"
|
||||
#include "core-impl/Telephony.h"
|
||||
#include "core-impl/utils.h"
|
||||
|
||||
|
@ -104,6 +106,42 @@ bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& forma
|
|||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::shared_ptr<Module> Module::createInstance(Type type) {
|
||||
switch (type) {
|
||||
case Module::Type::USB:
|
||||
return ndk::SharedRefBase::make<ModuleUsb>(type);
|
||||
case Type::DEFAULT:
|
||||
case Type::R_SUBMIX:
|
||||
default:
|
||||
return ndk::SharedRefBase::make<Module>(type);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
StreamIn::CreateInstance Module::getStreamInCreator(Type type) {
|
||||
switch (type) {
|
||||
case Type::USB:
|
||||
return StreamInUsb::createInstance;
|
||||
case Type::DEFAULT:
|
||||
case Type::R_SUBMIX:
|
||||
default:
|
||||
return StreamInStub::createInstance;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
StreamOut::CreateInstance Module::getStreamOutCreator(Type type) {
|
||||
switch (type) {
|
||||
case Type::USB:
|
||||
return StreamOutUsb::createInstance;
|
||||
case Type::DEFAULT:
|
||||
case Type::R_SUBMIX:
|
||||
default:
|
||||
return StreamOutStub::createInstance;
|
||||
}
|
||||
}
|
||||
|
||||
void Module::cleanUpPatch(int32_t patchId) {
|
||||
erase_all_values(mPatches, std::set<int32_t>{patchId});
|
||||
}
|
||||
|
@ -153,6 +191,7 @@ ndk::ScopedAStatus Module::createStreamContext(
|
|||
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,
|
||||
std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
|
||||
asyncCallback, outEventCallback, params);
|
||||
if (temp.isValid()) {
|
||||
|
@ -261,6 +300,7 @@ internal::Configuration& Module::getConfig() {
|
|||
break;
|
||||
case Type::USB:
|
||||
mConfig = std::move(internal::getUsbConfiguration());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *mConfig;
|
||||
|
@ -401,6 +441,8 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
|
|||
if (!mDebug.simulateDeviceConnections) {
|
||||
// In a real HAL here we would attempt querying the profiles from the device.
|
||||
LOG(ERROR) << __func__ << ": failed to query supported device profiles";
|
||||
// TODO: Check the return value when it is ready for actual devices.
|
||||
populateConnectedDevicePort(&connectedPort);
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
|
||||
|
@ -560,10 +602,9 @@ ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_ar
|
|||
}
|
||||
context.fillDescriptor(&_aidl_return->desc);
|
||||
std::shared_ptr<StreamIn> stream;
|
||||
// TODO: Add a mapping from module instance names to a corresponding 'createInstance'.
|
||||
if (auto status = StreamInStub::createInstance(in_args.sinkMetadata, std::move(context),
|
||||
mConfig->microphones, &stream);
|
||||
!status.isOk()) {
|
||||
ndk::ScopedAStatus status = getStreamInCreator(mType)(in_args.sinkMetadata, std::move(context),
|
||||
mConfig->microphones, &stream);
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
StreamWrapper streamWrapper(stream);
|
||||
|
@ -615,10 +656,9 @@ ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_
|
|||
}
|
||||
context.fillDescriptor(&_aidl_return->desc);
|
||||
std::shared_ptr<StreamOut> stream;
|
||||
// TODO: Add a mapping from module instance names to a corresponding 'createInstance'.
|
||||
if (auto status = StreamOutStub::createInstance(in_args.sourceMetadata, std::move(context),
|
||||
in_args.offloadInfo, &stream);
|
||||
!status.isOk()) {
|
||||
ndk::ScopedAStatus status = getStreamOutCreator(mType)(
|
||||
in_args.sourceMetadata, std::move(context), in_args.offloadInfo, &stream);
|
||||
if (!status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
StreamWrapper streamWrapper(stream);
|
||||
|
@ -696,6 +736,10 @@ ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPa
|
|||
}
|
||||
}
|
||||
|
||||
if (auto status = checkAudioPatchEndpointsMatch(sources, sinks); !status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
auto& patches = getConfig().patches;
|
||||
auto existing = patches.end();
|
||||
std::optional<decltype(mPatches)> patchesBackup;
|
||||
|
@ -1190,4 +1234,16 @@ bool Module::isMmapSupported() {
|
|||
return mIsMmapSupported.value();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) {
|
||||
LOG(DEBUG) << __func__ << ": do nothing and return ok";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::checkAudioPatchEndpointsMatch(
|
||||
const std::vector<AudioPortConfig*>& sources __unused,
|
||||
const std::vector<AudioPortConfig*>& sinks __unused) {
|
||||
LOG(DEBUG) << __func__ << ": do nothing and return ok";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using aidl::android::hardware::audio::common::SourceMetadata;
|
||||
using aidl::android::media::audio::common::AudioDevice;
|
||||
using aidl::android::media::audio::common::AudioOffloadInfo;
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
@ -68,6 +69,11 @@ DriverStub::DriverStub(const StreamContext& context, bool isInput)
|
|||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t DriverStub::setConnectedDevices(
|
||||
const std::vector<AudioDevice>& connectedDevices __unused) {
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
// static
|
||||
ndk::ScopedAStatus StreamInStub::createInstance(const SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context,
|
||||
|
|
|
@ -35,6 +35,10 @@ class Module : public BnModule {
|
|||
|
||||
explicit Module(Type type) : mType(type) {}
|
||||
|
||||
static std::shared_ptr<Module> createInstance(Type type);
|
||||
static StreamIn::CreateInstance getStreamInCreator(Type type);
|
||||
static StreamOut::CreateInstance getStreamOutCreator(Type type);
|
||||
|
||||
private:
|
||||
struct VendorDebug {
|
||||
static const std::string kForceTransientBurstName;
|
||||
|
@ -163,6 +167,17 @@ class Module : public BnModule {
|
|||
std::shared_ptr<sounddose::ISoundDose> mSoundDose;
|
||||
ndk::SpAIBinder mSoundDoseBinder;
|
||||
std::optional<bool> mIsMmapSupported;
|
||||
|
||||
protected:
|
||||
// If the module is unable to populate the connected device port correctly, the returned error
|
||||
// code must correspond to the errors of `IModule.connectedExternalDevice` method.
|
||||
virtual ndk::ScopedAStatus populateConnectedDevicePort(
|
||||
::aidl::android::media::audio::common::AudioPort* audioPort);
|
||||
// If the module finds that the patch endpoints configurations are not matched, the returned
|
||||
// error code must correspond to the errors of `IModule.setAudioPatch` method.
|
||||
virtual ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
|
||||
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
|
||||
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks);
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
|
47
audio/aidl/default/include/core-impl/ModuleUsb.h
Normal file
47
audio/aidl/default/include/core-impl/ModuleUsb.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core-impl/Module.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
class ModuleUsb : public Module {
|
||||
public:
|
||||
explicit ModuleUsb(Module::Type type) : Module(type) {}
|
||||
|
||||
private:
|
||||
// IModule interfaces
|
||||
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
|
||||
ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
|
||||
ndk::ScopedAStatus getMasterMute(bool* _aidl_return) override;
|
||||
ndk::ScopedAStatus setMasterMute(bool in_mute) override;
|
||||
ndk::ScopedAStatus getMasterVolume(float* _aidl_return) override;
|
||||
ndk::ScopedAStatus setMasterVolume(float in_volume) override;
|
||||
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
|
||||
ndk::ScopedAStatus setMicMute(bool in_mute) override;
|
||||
|
||||
// Module interfaces
|
||||
ndk::ScopedAStatus populateConnectedDevicePort(
|
||||
::aidl::android::media::audio::common::AudioPort* audioPort) override;
|
||||
ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
|
||||
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
|
||||
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks)
|
||||
override;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
|
@ -77,7 +77,8 @@ class StreamContext {
|
|||
StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
|
||||
const ::aidl::android::media::audio::common::AudioFormatDescription& format,
|
||||
const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
|
||||
std::unique_ptr<DataMQ> dataMQ, std::shared_ptr<IStreamCallback> asyncCallback,
|
||||
int sampleRate, std::unique_ptr<DataMQ> dataMQ,
|
||||
std::shared_ptr<IStreamCallback> asyncCallback,
|
||||
std::shared_ptr<IStreamOutEventCallback> outEventCallback,
|
||||
DebugParameters debugParameters)
|
||||
: mCommandMQ(std::move(commandMQ)),
|
||||
|
@ -85,6 +86,7 @@ class StreamContext {
|
|||
mReplyMQ(std::move(replyMQ)),
|
||||
mFormat(format),
|
||||
mChannelLayout(channelLayout),
|
||||
mSampleRate(sampleRate),
|
||||
mDataMQ(std::move(dataMQ)),
|
||||
mAsyncCallback(asyncCallback),
|
||||
mOutEventCallback(outEventCallback),
|
||||
|
@ -95,6 +97,7 @@ class StreamContext {
|
|||
mReplyMQ(std::move(other.mReplyMQ)),
|
||||
mFormat(other.mFormat),
|
||||
mChannelLayout(other.mChannelLayout),
|
||||
mSampleRate(other.mSampleRate),
|
||||
mDataMQ(std::move(other.mDataMQ)),
|
||||
mAsyncCallback(std::move(other.mAsyncCallback)),
|
||||
mOutEventCallback(std::move(other.mOutEventCallback)),
|
||||
|
@ -105,6 +108,7 @@ class StreamContext {
|
|||
mReplyMQ = std::move(other.mReplyMQ);
|
||||
mFormat = std::move(other.mFormat);
|
||||
mChannelLayout = std::move(other.mChannelLayout);
|
||||
mSampleRate = other.mSampleRate;
|
||||
mDataMQ = std::move(other.mDataMQ);
|
||||
mAsyncCallback = std::move(other.mAsyncCallback);
|
||||
mOutEventCallback = std::move(other.mOutEventCallback);
|
||||
|
@ -131,6 +135,7 @@ class StreamContext {
|
|||
}
|
||||
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
|
||||
int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
|
||||
int getSampleRate() const { return mSampleRate; }
|
||||
bool isValid() const;
|
||||
void reset();
|
||||
|
||||
|
@ -140,6 +145,7 @@ class StreamContext {
|
|||
std::unique_ptr<ReplyMQ> mReplyMQ;
|
||||
::aidl::android::media::audio::common::AudioFormatDescription mFormat;
|
||||
::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
|
||||
int mSampleRate;
|
||||
std::unique_ptr<DataMQ> mDataMQ;
|
||||
std::shared_ptr<IStreamCallback> mAsyncCallback;
|
||||
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
|
||||
|
@ -151,6 +157,11 @@ struct DriverInterface {
|
|||
virtual ~DriverInterface() = default;
|
||||
// This function is called once, on the main thread, before starting the worker thread.
|
||||
virtual ::android::status_t init() = 0;
|
||||
// This function is called from Binder pool thread. It must be done in a thread-safe manner
|
||||
// if this method and other methods in this interface share data.
|
||||
virtual ::android::status_t setConnectedDevices(
|
||||
const std::vector<::aidl::android::media::audio::common::AudioDevice>&
|
||||
connectedDevices) = 0;
|
||||
// All the functions below are called on the worker thread.
|
||||
virtual ::android::status_t drain(StreamDescriptor::DrainMode mode) = 0;
|
||||
virtual ::android::status_t flush() = 0;
|
||||
|
@ -370,6 +381,7 @@ class StreamCommonImpl : public StreamCommonInterface {
|
|||
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
|
||||
mWorker->setIsConnected(!devices.empty());
|
||||
mConnectedDevices = devices;
|
||||
mDriver->setConnectedDevices(devices);
|
||||
}
|
||||
ndk::ScopedAStatus updateMetadata(const Metadata& metadata);
|
||||
|
||||
|
|
|
@ -24,6 +24,9 @@ class DriverStub : public DriverInterface {
|
|||
public:
|
||||
DriverStub(const StreamContext& context, bool isInput);
|
||||
::android::status_t init() override;
|
||||
::android::status_t setConnectedDevices(
|
||||
const std::vector<::aidl::android::media::audio::common::AudioDevice>& connectedDevices)
|
||||
override;
|
||||
::android::status_t drain(StreamDescriptor::DrainMode) override;
|
||||
::android::status_t flush() override;
|
||||
::android::status_t pause() override;
|
||||
|
|
94
audio/aidl/default/include/core-impl/StreamUsb.h
Normal file
94
audio/aidl/default/include/core-impl/StreamUsb.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
|
||||
|
||||
#include "core-impl/Stream.h"
|
||||
|
||||
extern "C" {
|
||||
#include <tinyalsa/pcm.h>
|
||||
#include "alsa_device_proxy.h"
|
||||
}
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
class DriverUsb : public DriverInterface {
|
||||
public:
|
||||
DriverUsb(const StreamContext& context, bool isInput);
|
||||
::android::status_t init() override;
|
||||
::android::status_t setConnectedDevices(
|
||||
const std::vector<::aidl::android::media::audio::common::AudioDevice>& connectedDevices)
|
||||
override;
|
||||
::android::status_t drain(StreamDescriptor::DrainMode) override;
|
||||
::android::status_t flush() override;
|
||||
::android::status_t pause() override;
|
||||
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
|
||||
int32_t* latencyMs) override;
|
||||
::android::status_t standby() override;
|
||||
|
||||
private:
|
||||
::android::status_t exitStandby();
|
||||
|
||||
std::mutex mLock;
|
||||
|
||||
const size_t mFrameSizeBytes;
|
||||
std::optional<struct pcm_config> mConfig;
|
||||
const bool mIsInput;
|
||||
// Cached device addresses for connected devices.
|
||||
std::vector<::aidl::android::media::audio::common::AudioDeviceAddress> mConnectedDevices
|
||||
GUARDED_BY(mLock);
|
||||
std::vector<std::shared_ptr<alsa_device_proxy>> mAlsaDeviceProxies GUARDED_BY(mLock);
|
||||
bool mIsStandby = false;
|
||||
};
|
||||
|
||||
class StreamInUsb final : public StreamIn {
|
||||
ndk::ScopedAStatus getActiveMicrophones(
|
||||
std::vector<MicrophoneDynamicInfo>* _aidl_return) override;
|
||||
|
||||
public:
|
||||
static ndk::ScopedAStatus createInstance(
|
||||
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context, const std::vector<MicrophoneInfo>& microphones,
|
||||
std::shared_ptr<StreamIn>* result);
|
||||
|
||||
private:
|
||||
friend class ndk::SharedRefBase;
|
||||
StreamInUsb(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context, const std::vector<MicrophoneInfo>& microphones);
|
||||
};
|
||||
|
||||
class StreamOutUsb final : public StreamOut {
|
||||
public:
|
||||
static ndk::ScopedAStatus createInstance(
|
||||
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
|
||||
StreamContext&& context,
|
||||
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
|
||||
offloadInfo,
|
||||
std::shared_ptr<StreamOut>* result);
|
||||
|
||||
private:
|
||||
friend class ndk::SharedRefBase;
|
||||
StreamOutUsb(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
|
||||
StreamContext&& context,
|
||||
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
|
||||
offloadInfo);
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
|
@ -17,6 +17,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
|
@ -101,4 +102,21 @@ std::vector<T*> selectByIds(std::vector<T>& v, const std::vector<int32_t>& ids,
|
|||
return result;
|
||||
}
|
||||
|
||||
// Assuming that M is a map whose keys' type is K and values' type is V,
|
||||
// return the corresponding value of the given key from the map or default
|
||||
// value if the key is not found.
|
||||
template <typename M, typename K, typename V>
|
||||
auto findValueOrDefault(const M& m, const K& key, V defaultValue) {
|
||||
auto it = m.find(key);
|
||||
return it == m.end() ? defaultValue : it->second;
|
||||
}
|
||||
|
||||
// Assuming that M is a map whose keys' type is K, return the given key if it
|
||||
// is found from the map or default value.
|
||||
template <typename M, typename K>
|
||||
auto findKeyOrDefault(const M& m, const K& key, K defaultValue) {
|
||||
auto it = m.find(key);
|
||||
return it == m.end() ? defaultValue : key;
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
|
|
@ -25,9 +25,11 @@
|
|||
|
||||
#include "core-impl/Config.h"
|
||||
#include "core-impl/Module.h"
|
||||
#include "core-impl/ModuleUsb.h"
|
||||
|
||||
using aidl::android::hardware::audio::core::Config;
|
||||
using aidl::android::hardware::audio::core::Module;
|
||||
using aidl::android::hardware::audio::core::ModuleUsb;
|
||||
|
||||
int main() {
|
||||
// Random values are used in the implementation.
|
||||
|
@ -48,7 +50,7 @@ int main() {
|
|||
|
||||
// Make modules
|
||||
auto createModule = [](Module::Type type, const std::string& instance) {
|
||||
auto module = ndk::SharedRefBase::make<Module>(type);
|
||||
auto module = Module::createInstance(type);
|
||||
ndk::SpAIBinder moduleBinder = module->asBinder();
|
||||
const std::string moduleName = std::string(Module::descriptor).append("/").append(instance);
|
||||
AIBinder_setMinSchedulerPolicy(moduleBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
|
||||
|
|
183
audio/aidl/default/usb/ModuleUsb.cpp
Normal file
183
audio/aidl/default/usb/ModuleUsb.cpp
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "AHAL_ModuleUsb"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Utils.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <tinyalsa/asoundlib.h>
|
||||
|
||||
#include "UsbAlsaUtils.h"
|
||||
#include "core-impl/ModuleUsb.h"
|
||||
|
||||
extern "C" {
|
||||
#include "alsa_device_profile.h"
|
||||
}
|
||||
|
||||
using aidl::android::media::audio::common::AudioChannelLayout;
|
||||
using aidl::android::media::audio::common::AudioDeviceAddress;
|
||||
using aidl::android::media::audio::common::AudioDeviceDescription;
|
||||
using aidl::android::media::audio::common::AudioDeviceType;
|
||||
using aidl::android::media::audio::common::AudioFormatDescription;
|
||||
using aidl::android::media::audio::common::AudioFormatType;
|
||||
using aidl::android::media::audio::common::AudioPort;
|
||||
using aidl::android::media::audio::common::AudioPortConfig;
|
||||
using aidl::android::media::audio::common::AudioPortExt;
|
||||
using aidl::android::media::audio::common::AudioProfile;
|
||||
using android::hardware::audio::common::isUsbInputDeviceType;
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<AudioChannelLayout> populateChannelMasksFromProfile(const alsa_device_profile* profile,
|
||||
bool isInput) {
|
||||
std::vector<AudioChannelLayout> channels;
|
||||
for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) {
|
||||
auto layoutMask =
|
||||
usb::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput);
|
||||
if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) {
|
||||
channels.push_back(layoutMask);
|
||||
}
|
||||
auto indexMask = usb::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]);
|
||||
if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) {
|
||||
channels.push_back(indexMask);
|
||||
}
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
std::vector<int> populateSampleRatesFromProfile(const alsa_device_profile* profile) {
|
||||
std::vector<int> sampleRates;
|
||||
for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
|
||||
profile->sample_rates[i] != 0;
|
||||
i++) {
|
||||
sampleRates.push_back(profile->sample_rates[i]);
|
||||
}
|
||||
return sampleRates;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
|
||||
*_aidl_return = nullptr;
|
||||
LOG(DEBUG) << __func__ << ": returning null";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
|
||||
*_aidl_return = nullptr;
|
||||
LOG(DEBUG) << __func__ << ": returning null";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::getMasterMute(bool* _aidl_return __unused) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::setMasterMute(bool in_mute __unused) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::getMasterVolume(float* _aidl_return __unused) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::setMasterVolume(float in_volume __unused) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::getMicMute(bool* _aidl_return __unused) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::setMicMute(bool in_mute __unused) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::populateConnectedDevicePort(AudioPort* audioPort) {
|
||||
if (audioPort->ext.getTag() != AudioPortExt::Tag::device) {
|
||||
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a device port";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
auto& devicePort = audioPort->ext.get<AudioPortExt::Tag::device>();
|
||||
if (devicePort.device.type.connection != AudioDeviceDescription::CONNECTION_USB) {
|
||||
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a usb device port";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
if (devicePort.device.address.getTag() != AudioDeviceAddress::Tag::alsa) {
|
||||
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not using alsa address";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
auto& alsaAddress = devicePort.device.address.get<AudioDeviceAddress::Tag::alsa>();
|
||||
if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) {
|
||||
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " invalid alsa address";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
const bool isInput = isUsbInputDeviceType(devicePort.device.type.type);
|
||||
alsa_device_profile profile;
|
||||
profile_init(&profile, isInput ? PCM_IN : PCM_OUT);
|
||||
if (!profile_read_device_info(&profile)) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
|
||||
std::vector<AudioChannelLayout> channels = populateChannelMasksFromProfile(&profile, isInput);
|
||||
std::vector<int> sampleRates = populateSampleRatesFromProfile(&profile);
|
||||
|
||||
for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
|
||||
profile.formats[i] != 0;
|
||||
++i) {
|
||||
auto audioFormatDescription =
|
||||
usb::legacy2aidl_pcm_format_AudioFormatDescription(profile.formats[i]);
|
||||
if (audioFormatDescription.type == AudioFormatType::DEFAULT) {
|
||||
LOG(WARNING) << __func__ << ": unknown pcm type=" << profile.formats[i];
|
||||
continue;
|
||||
}
|
||||
AudioProfile audioProfile = {.format = audioFormatDescription,
|
||||
.channelMasks = channels,
|
||||
.sampleRates = sampleRates};
|
||||
audioPort->profiles.push_back(std::move(audioProfile));
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch(
|
||||
const std::vector<AudioPortConfig*>& sources, const std::vector<AudioPortConfig*>& sinks) {
|
||||
for (const auto& source : sources) {
|
||||
for (const auto& sink : sinks) {
|
||||
if (source->sampleRate != sink->sampleRate ||
|
||||
source->channelMask != sink->channelMask || source->format != sink->format) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": mismatch port configuration, source=" << source->toString()
|
||||
<< ", sink=" << sink->toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
242
audio/aidl/default/usb/StreamUsb.cpp
Normal file
242
audio/aidl/default/usb/StreamUsb.cpp
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "AHAL_StreamUsb"
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "UsbAlsaUtils.h"
|
||||
#include "core-impl/Module.h"
|
||||
#include "core-impl/StreamUsb.h"
|
||||
|
||||
extern "C" {
|
||||
#include "alsa_device_profile.h"
|
||||
}
|
||||
|
||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using aidl::android::hardware::audio::common::SourceMetadata;
|
||||
using aidl::android::media::audio::common::AudioDevice;
|
||||
using aidl::android::media::audio::common::AudioDeviceAddress;
|
||||
using aidl::android::media::audio::common::AudioOffloadInfo;
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
DriverUsb::DriverUsb(const StreamContext& context, bool isInput)
|
||||
: mFrameSizeBytes(context.getFrameSize()), mIsInput(isInput) {
|
||||
struct pcm_config config;
|
||||
config.channels = usb::getChannelCountFromChannelMask(context.getChannelLayout(), isInput);
|
||||
if (config.channels == 0) {
|
||||
LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString();
|
||||
return;
|
||||
}
|
||||
config.format = usb::aidl2legacy_AudioFormatDescription_pcm_format(context.getFormat());
|
||||
if (config.format == PCM_FORMAT_INVALID) {
|
||||
LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
|
||||
return;
|
||||
}
|
||||
config.rate = context.getSampleRate();
|
||||
if (config.rate == 0) {
|
||||
LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
|
||||
return;
|
||||
}
|
||||
mConfig = config;
|
||||
}
|
||||
|
||||
::android::status_t DriverUsb::init() {
|
||||
return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
|
||||
}
|
||||
|
||||
::android::status_t DriverUsb::setConnectedDevices(
|
||||
const std::vector<AudioDevice>& connectedDevices) {
|
||||
if (mIsInput && connectedDevices.size() > 1) {
|
||||
LOG(ERROR) << __func__ << ": wrong device size(" << connectedDevices.size()
|
||||
<< ") for input stream";
|
||||
return ::android::BAD_VALUE;
|
||||
}
|
||||
for (const auto& connectedDevice : connectedDevices) {
|
||||
if (connectedDevice.address.getTag() != AudioDeviceAddress::alsa) {
|
||||
LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
|
||||
return ::android::BAD_VALUE;
|
||||
}
|
||||
}
|
||||
std::lock_guard guard(mLock);
|
||||
mAlsaDeviceProxies.clear();
|
||||
mConnectedDevices.clear();
|
||||
for (const auto& connectedDevice : connectedDevices) {
|
||||
mConnectedDevices.push_back(connectedDevice.address);
|
||||
}
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t DriverUsb::drain(StreamDescriptor::DrainMode) {
|
||||
usleep(1000);
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t DriverUsb::flush() {
|
||||
usleep(1000);
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t DriverUsb::pause() {
|
||||
usleep(1000);
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t DriverUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
|
||||
int32_t* latencyMs) {
|
||||
if (!mConfig.has_value() || mConnectedDevices.empty()) {
|
||||
return ::android::NO_INIT;
|
||||
}
|
||||
if (mIsStandby) {
|
||||
if (::android::status_t status = exitStandby(); status != ::android::OK) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
std::vector<std::shared_ptr<alsa_device_proxy>> alsaDeviceProxies;
|
||||
{
|
||||
std::lock_guard guard(mLock);
|
||||
alsaDeviceProxies = mAlsaDeviceProxies;
|
||||
}
|
||||
const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
|
||||
if (mIsInput) {
|
||||
// For input case, only support single device.
|
||||
proxy_read(alsaDeviceProxies[0].get(), buffer, bytesToTransfer);
|
||||
} else {
|
||||
for (auto& proxy : alsaDeviceProxies) {
|
||||
proxy_write(proxy.get(), buffer, bytesToTransfer);
|
||||
}
|
||||
}
|
||||
*actualFrameCount = frameCount;
|
||||
*latencyMs = Module::kLatencyMs;
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t DriverUsb::standby() {
|
||||
if (!mIsStandby) {
|
||||
std::lock_guard guard(mLock);
|
||||
mAlsaDeviceProxies.clear();
|
||||
mIsStandby = true;
|
||||
}
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t DriverUsb::exitStandby() {
|
||||
std::vector<AudioDeviceAddress> connectedDevices;
|
||||
{
|
||||
std::lock_guard guard(mLock);
|
||||
connectedDevices = mConnectedDevices;
|
||||
}
|
||||
std::vector<std::shared_ptr<alsa_device_proxy>> alsaDeviceProxies;
|
||||
for (const auto& device : connectedDevices) {
|
||||
alsa_device_profile profile;
|
||||
profile.card = device.get<AudioDeviceAddress::alsa>()[0];
|
||||
profile.device = device.get<AudioDeviceAddress::alsa>()[1];
|
||||
if (!profile_read_device_info(&profile)) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": unable to read device info, device address=" << device.toString();
|
||||
return ::android::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
auto proxy = std::shared_ptr<alsa_device_proxy>(new alsa_device_proxy(),
|
||||
[](alsa_device_proxy* proxy) {
|
||||
proxy_close(proxy);
|
||||
free(proxy);
|
||||
});
|
||||
// Always ask for alsa configure as required since the configuration should be supported
|
||||
// by the connected device. That is guaranteed by `setAudioPortConfig` and
|
||||
// `setAudioPatch`.
|
||||
if (int err =
|
||||
proxy_prepare(proxy.get(), &profile, &mConfig.value(), true /*is_bit_perfect*/);
|
||||
err != 0) {
|
||||
LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device.toString()
|
||||
<< " error=" << err;
|
||||
return ::android::UNKNOWN_ERROR;
|
||||
}
|
||||
alsaDeviceProxies.push_back(std::move(proxy));
|
||||
}
|
||||
{
|
||||
std::lock_guard guard(mLock);
|
||||
mAlsaDeviceProxies = alsaDeviceProxies;
|
||||
}
|
||||
mIsStandby = false;
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
// static
|
||||
ndk::ScopedAStatus StreamInUsb::createInstance(const SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context,
|
||||
const std::vector<MicrophoneInfo>& microphones,
|
||||
std::shared_ptr<StreamIn>* result) {
|
||||
std::shared_ptr<StreamIn> stream =
|
||||
ndk::SharedRefBase::make<StreamInUsb>(sinkMetadata, std::move(context), microphones);
|
||||
if (auto status = initInstance(stream); !status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
*result = std::move(stream);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
StreamInUsb::StreamInUsb(const SinkMetadata& sinkMetadata, StreamContext&& context,
|
||||
const std::vector<MicrophoneInfo>& microphones)
|
||||
: StreamIn(
|
||||
sinkMetadata, std::move(context),
|
||||
[](const StreamContext& ctx) -> DriverInterface* {
|
||||
return new DriverUsb(ctx, true /*isInput*/);
|
||||
},
|
||||
[](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
|
||||
// The default worker implementation is used.
|
||||
return new StreamInWorker(ctx, driver);
|
||||
},
|
||||
microphones) {}
|
||||
|
||||
ndk::ScopedAStatus StreamInUsb::getActiveMicrophones(
|
||||
std::vector<MicrophoneDynamicInfo>* _aidl_return __unused) {
|
||||
LOG(DEBUG) << __func__ << ": not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
// static
|
||||
ndk::ScopedAStatus StreamOutUsb::createInstance(const SourceMetadata& sourceMetadata,
|
||||
StreamContext&& context,
|
||||
const std::optional<AudioOffloadInfo>& offloadInfo,
|
||||
std::shared_ptr<StreamOut>* result) {
|
||||
if (offloadInfo.has_value()) {
|
||||
LOG(ERROR) << __func__ << ": offload is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
std::shared_ptr<StreamOut> stream =
|
||||
ndk::SharedRefBase::make<StreamOutUsb>(sourceMetadata, std::move(context), offloadInfo);
|
||||
if (auto status = initInstance(stream); !status.isOk()) {
|
||||
return status;
|
||||
}
|
||||
*result = std::move(stream);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context,
|
||||
const std::optional<AudioOffloadInfo>& offloadInfo)
|
||||
: StreamOut(
|
||||
sourceMetadata, std::move(context),
|
||||
[](const StreamContext& ctx) -> DriverInterface* {
|
||||
return new DriverUsb(ctx, false /*isInput*/);
|
||||
},
|
||||
[](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
|
||||
// The default worker implementation is used.
|
||||
return new StreamOutWorker(ctx, driver);
|
||||
},
|
||||
offloadInfo) {}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
181
audio/aidl/default/usb/UsbAlsaUtils.cpp
Normal file
181
audio/aidl/default/usb/UsbAlsaUtils.cpp
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 <map>
|
||||
#include <set>
|
||||
|
||||
#include <Utils.h>
|
||||
#include <aidl/android/media/audio/common/AudioFormatType.h>
|
||||
#include <aidl/android/media/audio/common/PcmType.h>
|
||||
|
||||
#include "UsbAlsaUtils.h"
|
||||
#include "core-impl/utils.h"
|
||||
|
||||
using aidl::android::media::audio::common::AudioChannelLayout;
|
||||
using aidl::android::media::audio::common::AudioFormatDescription;
|
||||
using aidl::android::media::audio::common::AudioFormatType;
|
||||
using aidl::android::media::audio::common::PcmType;
|
||||
using android::hardware::audio::common::getChannelCount;
|
||||
|
||||
namespace aidl::android::hardware::audio::core::usb {
|
||||
|
||||
namespace {
|
||||
|
||||
using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
|
||||
using AudioFormatDescToPcmFormatMap = std::map<AudioFormatDescription, enum pcm_format>;
|
||||
using PcmFormatToAudioFormatDescMap = std::map<enum pcm_format, AudioFormatDescription>;
|
||||
|
||||
static const AudioChannelLayout INVALID_CHANNEL_LAYOUT =
|
||||
AudioChannelLayout::make<AudioChannelLayout::Tag::invalid>(0);
|
||||
|
||||
#define DEFINE_CHANNEL_LAYOUT_MASK(n) \
|
||||
AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(AudioChannelLayout::LAYOUT_##n)
|
||||
|
||||
static const std::set<AudioChannelLayout> SUPPORTED_OUT_CHANNEL_LAYOUTS = {
|
||||
DEFINE_CHANNEL_LAYOUT_MASK(MONO), DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
|
||||
DEFINE_CHANNEL_LAYOUT_MASK(2POINT1), DEFINE_CHANNEL_LAYOUT_MASK(QUAD),
|
||||
DEFINE_CHANNEL_LAYOUT_MASK(PENTA), DEFINE_CHANNEL_LAYOUT_MASK(5POINT1),
|
||||
DEFINE_CHANNEL_LAYOUT_MASK(6POINT1), DEFINE_CHANNEL_LAYOUT_MASK(7POINT1),
|
||||
DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2),
|
||||
};
|
||||
|
||||
static const std::set<AudioChannelLayout> SUPPORTED_IN_CHANNEL_LAYOUTS = {
|
||||
DEFINE_CHANNEL_LAYOUT_MASK(MONO),
|
||||
DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
|
||||
};
|
||||
|
||||
#define DEFINE_CHANNEL_INDEX_MASK(n) \
|
||||
AudioChannelLayout::make<AudioChannelLayout::Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##n)
|
||||
|
||||
static const std::set<AudioChannelLayout> SUPPORTED_INDEX_CHANNEL_LAYOUTS = {
|
||||
DEFINE_CHANNEL_INDEX_MASK(1), DEFINE_CHANNEL_INDEX_MASK(2), DEFINE_CHANNEL_INDEX_MASK(3),
|
||||
DEFINE_CHANNEL_INDEX_MASK(4), DEFINE_CHANNEL_INDEX_MASK(5), DEFINE_CHANNEL_INDEX_MASK(6),
|
||||
DEFINE_CHANNEL_INDEX_MASK(7), DEFINE_CHANNEL_INDEX_MASK(8), DEFINE_CHANNEL_INDEX_MASK(9),
|
||||
DEFINE_CHANNEL_INDEX_MASK(10), DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12),
|
||||
DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14), DEFINE_CHANNEL_INDEX_MASK(15),
|
||||
DEFINE_CHANNEL_INDEX_MASK(16), DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18),
|
||||
DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20), DEFINE_CHANNEL_INDEX_MASK(21),
|
||||
DEFINE_CHANNEL_INDEX_MASK(22), DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24),
|
||||
};
|
||||
|
||||
static AudioChannelCountToMaskMap make_ChannelCountToMaskMap(
|
||||
const std::set<AudioChannelLayout>& channelMasks) {
|
||||
AudioChannelCountToMaskMap channelMaskToCountMap;
|
||||
for (const auto& channelMask : channelMasks) {
|
||||
channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask);
|
||||
}
|
||||
return channelMaskToCountMap;
|
||||
}
|
||||
|
||||
const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() {
|
||||
static const AudioChannelCountToMaskMap outLayouts =
|
||||
make_ChannelCountToMaskMap(SUPPORTED_OUT_CHANNEL_LAYOUTS);
|
||||
return outLayouts;
|
||||
}
|
||||
|
||||
const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() {
|
||||
static const AudioChannelCountToMaskMap inLayouts =
|
||||
make_ChannelCountToMaskMap(SUPPORTED_IN_CHANNEL_LAYOUTS);
|
||||
return inLayouts;
|
||||
}
|
||||
|
||||
const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() {
|
||||
static const AudioChannelCountToMaskMap indexLayouts =
|
||||
make_ChannelCountToMaskMap(SUPPORTED_INDEX_CHANNEL_LAYOUTS);
|
||||
return indexLayouts;
|
||||
}
|
||||
|
||||
AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
|
||||
AudioFormatDescription result;
|
||||
result.type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
|
||||
auto result = make_AudioFormatDescription(AudioFormatType::PCM);
|
||||
result.pcm = pcm;
|
||||
return result;
|
||||
}
|
||||
|
||||
const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() {
|
||||
static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
|
||||
{make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
|
||||
{make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
|
||||
{make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_LE},
|
||||
{make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_3LE},
|
||||
{make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
|
||||
{make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
|
||||
};
|
||||
return formatDescToPcmFormatMap;
|
||||
}
|
||||
|
||||
static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap(
|
||||
const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) {
|
||||
PcmFormatToAudioFormatDescMap result;
|
||||
for (const auto& formatPair : formatDescToPcmFormatMap) {
|
||||
result.emplace(formatPair.second, formatPair.first);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() {
|
||||
static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap =
|
||||
make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap());
|
||||
return pcmFormatToFormatDescMap;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) {
|
||||
return findValueOrDefault(
|
||||
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
|
||||
channelCount, INVALID_CHANNEL_LAYOUT);
|
||||
}
|
||||
|
||||
AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) {
|
||||
return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount,
|
||||
INVALID_CHANNEL_LAYOUT);
|
||||
}
|
||||
|
||||
unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) {
|
||||
switch (channelMask.getTag()) {
|
||||
case AudioChannelLayout::Tag::layoutMask: {
|
||||
return findKeyOrDefault(
|
||||
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
|
||||
(unsigned int)getChannelCount(channelMask), 0u /*defaultValue*/);
|
||||
}
|
||||
case AudioChannelLayout::Tag::indexMask: {
|
||||
return findKeyOrDefault(getSupportedChannelIndexLayoutMap(),
|
||||
(unsigned int)getChannelCount(channelMask),
|
||||
0u /*defaultValue*/);
|
||||
}
|
||||
case AudioChannelLayout::Tag::none:
|
||||
case AudioChannelLayout::Tag::invalid:
|
||||
case AudioChannelLayout::Tag::voiceMask:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
AudioFormatDescription legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
|
||||
return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
|
||||
}
|
||||
|
||||
pcm_format aidl2legacy_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) {
|
||||
return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core::usb
|
39
audio/aidl/default/usb/UsbAlsaUtils.h
Normal file
39
audio/aidl/default/usb/UsbAlsaUtils.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
|
||||
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
|
||||
|
||||
extern "C" {
|
||||
#include <tinyalsa/pcm.h>
|
||||
}
|
||||
|
||||
namespace aidl::android::hardware::audio::core::usb {
|
||||
|
||||
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
|
||||
unsigned int channelCount, int isInput);
|
||||
::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
|
||||
unsigned int channelCount);
|
||||
unsigned int getChannelCountFromChannelMask(
|
||||
const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput);
|
||||
::aidl::android::media::audio::common::AudioFormatDescription
|
||||
legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);
|
||||
pcm_format aidl2legacy_AudioFormatDescription_pcm_format(
|
||||
const ::aidl::android::media::audio::common::AudioFormatDescription& aidl);
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core::usb
|
Loading…
Reference in a new issue