blob: 75ec22c90fd63ee679f29da1d749c9be358c3266 [file] [log] [blame] [edit]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/ddk/binding.h>
#include <lib/ddk/platform-defs.h>
#include <lib/simple-codec/simple-codec-server.h>
#include <algorithm>
#include <memory>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
namespace audio {
namespace audio_fidl = ::fuchsia::hardware::audio;
namespace signal_fidl = ::fuchsia::hardware::audio::signalprocessing;
// SimpleCodecServer methods.
zx_status_t SimpleCodecServer::CreateAndAddToDdkInternal() {
simple_codec_ = inspect_.GetRoot().CreateChild("simple_codec");
state_ = simple_codec_.CreateString("state", "created");
start_time_ = simple_codec_.CreateInt("start_time", 0);
number_of_channels_ = simple_codec_.CreateUint("number_of_channels", 0);
channels_to_use_bitmask_ = simple_codec_.CreateUint("channels_to_use_bitmask", 0);
frame_rate_ = simple_codec_.CreateUint("frame_rate", 0);
bits_per_slot_ = simple_codec_.CreateUint("bits_per_slot", 0);
bits_per_sample_ = simple_codec_.CreateUint("bits_per_sample", 0);
sample_format_ = simple_codec_.CreateString("sample_format", "not_set");
frame_format_ = simple_codec_.CreateString("frame_format", "not_set");
auto res = Initialize();
if (res.is_error()) {
return res.error_value();
}
loop_->StartThread();
driver_ids_ = res.value();
Info info = GetInfo();
simple_codec_.CreateString("manufacturer", info.manufacturer, &inspect_);
simple_codec_.CreateString("product", info.product_name, &inspect_);
if (!info.unique_id.empty()) {
simple_codec_.CreateString("unique_id", info.unique_id, &inspect_);
}
if (driver_ids_.instance_count != 0) {
if (info.unique_id.empty()) {
simple_codec_.CreateString("unique_id", std::to_string(driver_ids_.instance_count),
&inspect_);
}
zx_device_prop_t props[] = {
{BIND_PLATFORM_DEV_VID, 0, driver_ids_.vendor_id},
{BIND_PLATFORM_DEV_DID, 0, driver_ids_.device_id},
{BIND_CODEC_INSTANCE, 0, driver_ids_.instance_count},
};
return DdkAdd(ddk::DeviceAddArgs(info.product_name.c_str())
.set_props(props)
.set_inspect_vmo(inspect_.DuplicateVmo())
.set_flags(DEVICE_ADD_ALLOW_MULTI_COMPOSITE));
}
zx_device_prop_t props[] = {
{BIND_PLATFORM_DEV_VID, 0, driver_ids_.vendor_id},
{BIND_PLATFORM_DEV_DID, 0, driver_ids_.device_id},
};
return DdkAdd(ddk::DeviceAddArgs(info.product_name.c_str())
.set_props(props)
.set_inspect_vmo(inspect_.DuplicateVmo())
.set_flags(DEVICE_ADD_ALLOW_MULTI_COMPOSITE));
}
zx_status_t SimpleCodecServer::CodecConnect(zx::channel channel) {
return BindClient(std::move(channel), loop_->dispatcher());
}
// SimpleCodecServerInternal methods.
template <class T>
SimpleCodecServerInternal<T>::SimpleCodecServerInternal() {
plug_time_ = zx::clock::get_monotonic().get();
}
template <class T>
zx_status_t SimpleCodecServerInternal<T>::BindClient(zx::channel channel,
async_dispatcher_t* dispatcher) {
auto instance = std::make_unique<SimpleCodecServerInstance<SimpleCodecServer>>(std::move(channel),
dispatcher, this);
fbl::AutoLock lock(&instances_lock_);
instances_.push_back(std::move(instance));
return ZX_OK;
}
template <class T>
void SimpleCodecServerInternal<T>::OnUnbound(SimpleCodecServerInstance<T>* instance) {
fbl::AutoLock lock(&instances_lock_);
instances_.erase(*instance);
}
template <class T>
void SimpleCodecServerInternal<T>::Reset(Codec::ResetCallback callback,
SimpleCodecServerInstance<T>* instance) {
auto status = static_cast<T*>(this)->Reset();
if (status != ZX_OK) {
instance->binding_.Unbind();
fbl::AutoLock lock(&instances_lock_);
instances_.erase(*instance);
}
callback();
}
template <class T>
void SimpleCodecServerInternal<T>::Stop(Codec::StopCallback callback,
SimpleCodecServerInstance<T>* instance) {
auto status = static_cast<T*>(this)->Stop();
if (status != ZX_OK) {
static_cast<T*>(this)->state_.Set("stopped error");
instance->binding_.Unbind();
fbl::AutoLock lock(&instances_lock_);
instances_.erase(*instance);
} else {
static_cast<T*>(this)->state_.Set("stopped");
}
int64_t stop_time = zx::clock::get_monotonic().get();
callback(stop_time);
}
template <class T>
void SimpleCodecServerInternal<T>::Start(Codec::StartCallback callback,
SimpleCodecServerInstance<T>* instance) {
auto status = static_cast<T*>(this)->Start();
if (status != ZX_OK) {
static_cast<T*>(this)->state_.Set("start error");
instance->binding_.Unbind();
fbl::AutoLock lock(&instances_lock_);
instances_.erase(*instance);
} else {
static_cast<T*>(this)->state_.Set("started");
}
int64_t start_time = zx::clock::get_monotonic().get();
static_cast<T*>(this)->start_time_.Set(start_time);
callback(start_time);
}
template <class T>
void SimpleCodecServerInternal<T>::GetInfo(Codec::GetInfoCallback callback) {
callback(static_cast<T*>(this)->GetInfo());
}
template <class T>
void SimpleCodecServerInternal<T>::SignalProcessingConnect(
fidl::InterfaceRequest<signal_fidl::SignalProcessing> signal_processing) {
static_cast<T*>(this)->SignalProcessingConnect(std::move(signal_processing));
}
template <class T>
void SimpleCodecServerInternal<T>::WatchPlugState(Codec::WatchPlugStateCallback callback,
SimpleCodecServerInstance<T>* instance) {
if (SupportsAsyncPlugState()) {
WatchPlugState(std::move(callback));
} else {
if (instance->watch_plug_state_first_time_) {
instance->watch_plug_state_first_time_ = false;
fuchsia::hardware::audio::PlugState plug_state;
plug_state.set_plugged(true);
plug_state.set_plug_state_time(plug_time_);
callback(std::move(plug_state));
}
}
}
template <class T>
void SimpleCodecServerInternal<T>::GetPlugDetectCapabilities(
Codec::GetPlugDetectCapabilitiesCallback callback) {
if (SupportsAsyncPlugState()) {
callback(audio_fidl::PlugDetectCapabilities::CAN_ASYNC_NOTIFY);
} else {
callback(audio_fidl::PlugDetectCapabilities::HARDWIRED);
}
}
template <class T>
void SimpleCodecServerInternal<T>::IsBridgeable(audio_fidl::Codec::IsBridgeableCallback callback) {
callback(static_cast<T*>(this)->IsBridgeable());
}
template <class T>
void SimpleCodecServerInternal<T>::SetBridgedMode(bool enable_bridged_mode) {
static_cast<T*>(this)->SetBridgedMode(enable_bridged_mode);
}
template <class T>
void SimpleCodecServerInternal<T>::GetDaiFormats(Codec::GetDaiFormatsCallback callback) {
auto formats = static_cast<T*>(this)->GetDaiFormats();
std::vector<audio_fidl::DaiFrameFormat> frame_formats;
for (FrameFormat i : formats.frame_formats) {
audio_fidl::DaiFrameFormat frame_format;
frame_format.set_frame_format_standard(i);
frame_formats.emplace_back(std::move(frame_format));
}
audio_fidl::Codec_GetDaiFormats_Response response;
response.formats.emplace_back(audio_fidl::DaiSupportedFormats{
.number_of_channels = std::move(formats.number_of_channels),
.sample_formats = std::move(formats.sample_formats),
.frame_formats = std::move(frame_formats),
.frame_rates = std::move(formats.frame_rates),
.bits_per_slot = std::move(formats.bits_per_slot),
.bits_per_sample = std::move(formats.bits_per_sample),
});
audio_fidl::Codec_GetDaiFormats_Result result;
result.set_response(std::move(response));
callback(std::move(result));
}
template <class T>
void SimpleCodecServerInternal<T>::SetDaiFormat(audio_fidl::DaiFormat format,
Codec::SetDaiFormatCallback callback) {
DaiFormat format2 = {};
format2.number_of_channels = format.number_of_channels;
format2.channels_to_use_bitmask = format.channels_to_use_bitmask;
format2.sample_format = format.sample_format;
format2.frame_format = format.frame_format.frame_format_standard();
format2.frame_rate = format.frame_rate;
format2.bits_per_slot = format.bits_per_slot;
format2.bits_per_sample = format.bits_per_sample;
auto* thiz = static_cast<T*>(this);
thiz->number_of_channels_.Set(format2.number_of_channels);
thiz->channels_to_use_bitmask_.Set(format2.channels_to_use_bitmask);
thiz->frame_rate_.Set(format2.frame_rate);
thiz->bits_per_slot_.Set(format2.bits_per_slot);
thiz->bits_per_sample_.Set(format2.bits_per_sample);
using FidlSampleFormat = audio_fidl::DaiSampleFormat;
// clang-format off
switch (format2.sample_format) {
case FidlSampleFormat::PDM: thiz->sample_format_.Set("PDM"); break;
case FidlSampleFormat::PCM_SIGNED: thiz->sample_format_.Set("PCM_signed"); break;
case FidlSampleFormat::PCM_UNSIGNED: thiz->sample_format_.Set("PCM_unsigned"); break;
case FidlSampleFormat::PCM_FLOAT: thiz->sample_format_.Set("PCM_float"); break;
}
// clang-format on
using FidlFrameFormat = audio_fidl::DaiFrameFormatStandard;
// clang-format off
switch (format2.frame_format) {
case FidlFrameFormat::NONE: thiz->frame_format_.Set("NONE"); break;
case FidlFrameFormat::I2S: thiz->frame_format_.Set("I2S"); break;
case FidlFrameFormat::STEREO_LEFT: thiz->frame_format_.Set("Stereo_left"); break;
case FidlFrameFormat::STEREO_RIGHT: thiz->frame_format_.Set("Stereo_right"); break;
case FidlFrameFormat::TDM1: thiz->frame_format_.Set("TDM1"); break;
case FidlFrameFormat::TDM2: thiz->frame_format_.Set("TDM2"); break;
case FidlFrameFormat::TDM3: thiz->frame_format_.Set("TDM3"); break;
}
// clang-format on
audio_fidl::Codec_SetDaiFormat_Result result = {};
zx::status<CodecFormatInfo> format_info = thiz->SetDaiFormat(std::move(format2));
if (!format_info.is_ok()) {
thiz->state_.Set(std::string("Set DAI format error: ") + format_info.status_string());
result.set_err(format_info.status_value());
callback(std::move(result));
return;
}
audio_fidl::Codec_SetDaiFormat_Response response = {};
audio_fidl::CodecFormatInfo codec_state = {};
if (format_info->has_external_delay()) {
codec_state.set_external_delay(format_info->external_delay());
}
if (format_info->has_turn_on_delay()) {
codec_state.set_turn_on_delay(format_info->turn_on_delay());
}
if (format_info->has_turn_off_delay()) {
codec_state.set_turn_off_delay(format_info->turn_off_delay());
}
response.state = std::move(codec_state);
result.set_response(std::move(response));
callback(std::move(result));
}
template <class T>
void SimpleCodecServerInternal<T>::GetGainFormat(Codec::GetGainFormatCallback callback) {
auto format = static_cast<T*>(this)->GetGainFormat();
audio_fidl::GainFormat format2;
format2.set_type(audio_fidl::GainType::DECIBELS); // Only decibels in simple codec.
format2.set_min_gain(format.min_gain);
format2.set_max_gain(format.max_gain);
format2.set_gain_step(format.gain_step);
format2.set_can_mute(format.can_mute);
format2.set_can_agc(format.can_agc);
callback(std::move(format2));
}
template <class T>
void SimpleCodecServerInternal<T>::WatchGainState(Codec::WatchGainStateCallback callback,
SimpleCodecServerInstance<T>* instance) {
if (load_gain_state_first_time_) {
gain_state_ = static_cast<T*>(this)->GetGainState();
}
load_gain_state_first_time_ = false;
// Reply immediately if the gain state has been updated since the last call. Otherwise store the
// callback and reply the next time the gain state is updated. Only one hanging get may be
// outstanding at a time.
if (instance->gain_state_updated_) {
instance->gain_state_updated_ = false;
audio_fidl::GainState gain_state = {};
gain_state.set_muted(gain_state_.muted);
gain_state.set_agc_enabled(gain_state_.agc_enabled);
gain_state.set_gain_db(gain_state_.gain);
callback(std::move(gain_state));
} else if (!instance->gain_state_callback_) {
instance->gain_state_callback_.emplace(std::move(callback));
} else {
// The client called WatchGainState when another hanging get was pending.
instance->binding_.Unbind();
fbl::AutoLock lock(&instances_lock_);
instances_.erase(*instance);
}
}
template <class T>
void SimpleCodecServerInternal<T>::SetGainState(audio_fidl::GainState state) {
gain_state_.gain = state.gain_db();
gain_state_.muted = state.muted();
gain_state_.agc_enabled = state.agc_enabled();
static_cast<T*>(this)->SetGainState(gain_state_);
fbl::AutoLock lock(&instances_lock_);
for (auto& iter : instances_) {
audio_fidl::GainState state2;
state.Clone(&state2);
iter.GainStateUpdated(std::move(state2));
}
}
// SimpleCodecServerInstance methods.
template <class T>
void SimpleCodecServerInstance<T>::GainStateUpdated(audio_fidl::GainState gain_state) {
if (gain_state_callback_) {
(*gain_state_callback_)(std::move(gain_state));
gain_state_callback_.reset();
gain_state_updated_ = false;
} else {
// WatchGainState will immediately reply with the latest gain state next the time it is called.
gain_state_updated_ = true;
}
}
template class SimpleCodecServerInternal<SimpleCodecServer>;
} // namespace audio