blob: b3e73c673753c2082f7d356a981b0f7f876c73c4 [file] [log] [blame]
// Copyright 2018 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/async/dispatcher.h>
#include <lib/fidl/llcpp/server.h>
#include <lib/simple-audio-stream/simple-audio-stream.h>
#include <lib/zx/clock.h>
#include <lib/zx/process.h>
#include <lib/zx/thread.h>
#include <zircon/device/audio.h>
#include <limits>
#include <utility>
#include <audio-proto-utils/format-utils.h>
#include <ddk/debug.h>
namespace audio {
void SimpleAudioStream::Shutdown() {
if (!shutting_down_.exchange(true)) {
loop_.Shutdown();
// We have shutdown our loop, it is now safe to assert we are holding the domain token.
ScopedToken t(domain_token());
{
// Now we explicitly destroy the channels.
fbl::AutoLock channel_lock(&channel_lock_);
DeactivateRingBufferChannel(rb_channel_.get());
stream_channels_.clear();
stream_channel_ = nullptr;
}
ShutdownHook();
}
}
zx_status_t SimpleAudioStream::CreateInternal() {
zx_status_t res;
{
// We have not created the domain yet, it should be safe to pretend that
// we have the token (since we know that no dispatches are going to be
// invoked from the non-existent domain at this point)
ScopedToken t(domain_token());
res = Init();
if (res != ZX_OK) {
zxlogf(ERROR, "Init failure in %s (res %d)", __PRETTY_FUNCTION__, res);
return res;
}
// If no subclass has set this, we need to do so here.
if (plug_time_ == 0) {
plug_time_ = zx::clock::get_monotonic().get();
}
}
// TODO(fxbug.dev/37372): Add profile configuration.
// This single threaded dispatcher serializes the FIDL server implementation by this class.
loop_.StartThread("simple-audio-stream-loop");
res = PublishInternal();
if (res != ZX_OK) {
zxlogf(ERROR, "Publish failure in %s (res %d)", __PRETTY_FUNCTION__, res);
return res;
}
return ZX_OK;
}
zx_status_t SimpleAudioStream::PublishInternal() {
device_name_[sizeof(device_name_) - 1] = 0;
if (!strlen(device_name_)) {
zxlogf(ERROR, "Zero-length device name in %s", __PRETTY_FUNCTION__);
return ZX_ERR_BAD_STATE;
}
// If we succeed in adding our device, add an explicit reference to
// ourselves to represent the reference now being held by the DDK. We will
// get this reference back when the DDK (eventually) calls release.
zx_status_t res = DdkAdd(device_name_);
if (res == ZX_OK) {
AddRef();
}
return res;
}
// Called by a child subclass during Init, to establish the properties of this plug.
// Caller must include only flags defined for audio_stream_cmd_plug_detect_resp_t.
void SimpleAudioStream::SetInitialPlugState(audio_pd_notify_flags_t initial_state) {
audio_pd_notify_flags_t known_flags =
AUDIO_PDNF_HARDWIRED | AUDIO_PDNF_CAN_NOTIFY | AUDIO_PDNF_PLUGGED;
ZX_DEBUG_ASSERT((initial_state & known_flags) == initial_state);
pd_flags_ = initial_state;
plug_time_ = zx::clock::get_monotonic().get();
}
// Called by a child subclass when a dynamic plug state change occurs.
// Special behavior if this isn't actually a change, or if we should not be able to unplug.
zx_status_t SimpleAudioStream::SetPlugState(bool plugged) {
if (plugged == ((pd_flags_ & AUDIO_PDNF_PLUGGED) != 0)) {
return ZX_OK;
}
ZX_DEBUG_ASSERT(((pd_flags_ & AUDIO_PDNF_HARDWIRED) == 0) || plugged);
if (plugged) {
pd_flags_ |= AUDIO_PDNF_PLUGGED;
} else {
pd_flags_ &= ~AUDIO_PDNF_PLUGGED;
}
plug_time_ = zx::clock::get_monotonic().get();
if (pd_flags_ & AUDIO_PDNF_CAN_NOTIFY) {
return NotifyPlugDetect();
}
return ZX_OK;
}
// Asynchronously notify of plug state changes.
zx_status_t SimpleAudioStream::NotifyPlugDetect() {
auto builder = audio_fidl::PlugState::UnownedBuilder();
fidl::aligned<bool> plugged = pd_flags_ & AUDIO_PDNF_PLUGGED;
builder.set_plugged(fidl::unowned_ptr(&plugged));
builder.set_plug_state_time(fidl::unowned_ptr(&plug_time_));
fbl::AutoLock channel_lock(&channel_lock_);
for (auto& channel : stream_channels_) {
if (channel.plug_completer_) {
channel.plug_completer_->Reply(builder.build());
channel.plug_completer_.reset();
}
}
return ZX_OK;
}
zx_status_t SimpleAudioStream::NotifyPosition(const audio_proto::RingBufPositionNotify& notif) {
fbl::AutoLock channel_lock(&channel_lock_);
if (!expected_notifications_per_ring_.load() || (rb_channel_ == nullptr)) {
return ZX_ERR_BAD_STATE;
}
audio_fidl::RingBufferPositionInfo position_info = {};
position_info.position = notif.ring_buffer_pos;
position_info.timestamp = notif.monotonic_time;
fbl::AutoLock position_lock(&position_lock_);
if (position_completer_) {
position_completer_->Reply(position_info);
position_completer_.reset();
}
return ZX_OK;
}
void SimpleAudioStream::DdkUnbind(ddk::UnbindTxn txn) {
Shutdown();
// TODO(johngro): We need to signal our SimpleAudioStream owner to let them
// know that we have been unbound and are in the process of shutting down.
// Unpublish our device node.
txn.Reply();
}
void SimpleAudioStream::DdkRelease() {
// Recover our ref from the DDK, then let it fall out of scope.
auto thiz = fbl::ImportFromRawPtr(this);
}
void SimpleAudioStream::DdkSuspend(ddk::SuspendTxn txn) {
// TODO(fxbug.dev/42613): Implement proper power management based on the requested state.
Shutdown();
txn.Reply(ZX_OK, txn.requested_state());
}
void SimpleAudioStream::GetChannel(GetChannelCompleter::Sync& completer) {
fbl::AutoLock channel_lock(&channel_lock_);
// Attempt to allocate a new driver channel and bind it to us. If we don't
// already have an stream_channel_, flag this channel is the privileged
// connection (The connection which is allowed to do things like change
// formats).
bool privileged = (stream_channel_ == nullptr);
zx::channel stream_channel_local;
zx::channel stream_channel_remote;
auto status = zx::channel::create(0, &stream_channel_local, &stream_channel_remote);
if (status != ZX_OK) {
zxlogf(ERROR, "Could not create channel in %s", __PRETTY_FUNCTION__);
completer.Close(ZX_ERR_NO_MEMORY);
return;
}
auto stream_channel = StreamChannel::Create<StreamChannel>(this);
// We keep alive all channels in stream_channels_ (protected by channel_lock_).
stream_channels_.push_back(stream_channel);
fidl::OnUnboundFn<audio_fidl::StreamConfig::Interface> on_unbound =
[this, stream_channel](audio_fidl::StreamConfig::Interface*, fidl::UnbindInfo, zx::channel) {
ScopedToken t(domain_token());
fbl::AutoLock channel_lock(&channel_lock_);
this->DeactivateStreamChannel(stream_channel.get());
};
fidl::BindServer<audio_fidl::StreamConfig::Interface>(
dispatcher(), std::move(stream_channel_local), stream_channel.get(), std::move(on_unbound));
if (privileged) {
ZX_DEBUG_ASSERT(stream_channel_ == nullptr);
stream_channel_ = stream_channel;
}
completer.Reply(std::move(stream_channel_remote));
}
void SimpleAudioStream::DeactivateStreamChannel(StreamChannel* channel) {
if (stream_channel_.get() == channel) {
stream_channel_ = nullptr;
}
// Any pending LLCPP completer must be either replied to or closed before we destroy it.
if (channel->plug_completer_.has_value()) {
channel->plug_completer_->Close(ZX_ERR_INTERNAL);
}
channel->plug_completer_.reset();
// Any pending LLCPP completer must be either replied to or closed before we destroy it.
if (channel->gain_completer_.has_value()) {
channel->gain_completer_->Close(ZX_ERR_INTERNAL);
}
channel->gain_completer_.reset();
stream_channels_.erase(*channel); // Must be last since we may destruct *channel.
}
void SimpleAudioStream::DeactivateRingBufferChannel(const Channel* channel) {
if (rb_channel_.get() == channel) {
if (rb_started_) {
Stop();
rb_started_ = false;
}
rb_fetched_ = false;
expected_notifications_per_ring_.store(0);
{
fbl::AutoLock position_lock(&position_lock_);
// Any pending LLCPP completer must be either replied to or closed before we destroy it.
if (position_completer_.has_value()) {
position_completer_->Close(ZX_ERR_INTERNAL);
}
position_completer_.reset();
}
rb_channel_ = nullptr;
}
}
void SimpleAudioStream::CreateRingBuffer(
StreamChannel* channel, audio_fidl::Format format, zx::channel ring_buffer,
audio_fidl::StreamConfig::Interface::CreateRingBufferCompleter::Sync& completer) {
ScopedToken t(domain_token());
zx::channel rb_channel_local;
zx::channel rb_channel_remote;
bool found_one = false;
// On errors we call Close() and ring_buffer goes out of scope closing the channel.
// Only the privileged stream channel is allowed to change the format.
{
fbl::AutoLock channel_lock(&channel_lock_);
if (channel != stream_channel_.get()) {
zxlogf(ERROR, "Unprivileged channel cannot set the format");
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
}
auto format_v1 = format.pcm_format();
audio_sample_format_t sample_format = audio::utils::GetSampleFormat(
format_v1.valid_bits_per_sample, 8 * format_v1.bytes_per_sample);
if (sample_format == 0) {
zxlogf(ERROR, "Unsupported format: Invalid bits per sample (%u/%u)\n",
format_v1.valid_bits_per_sample, 8 * format_v1.bytes_per_sample);
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
if (format_v1.sample_format == audio_fidl::SampleFormat::PCM_FLOAT) {
sample_format = AUDIO_SAMPLE_FORMAT_32BIT_FLOAT;
if (format_v1.valid_bits_per_sample != 32 || format_v1.bytes_per_sample != 4) {
zxlogf(ERROR, "Unsupported format: Not 32 per sample/channel for float\n");
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
}
if (format_v1.sample_format == audio_fidl::SampleFormat::PCM_UNSIGNED) {
sample_format |= AUDIO_SAMPLE_FORMAT_FLAG_UNSIGNED;
}
// Check the format for compatibility
for (const auto& fmt : supported_formats_) {
if (audio::utils::FormatIsCompatible(format_v1.frame_rate,
static_cast<uint16_t>(format_v1.number_of_channels),
sample_format, fmt)) {
found_one = true;
break;
}
}
if (!found_one) {
zxlogf(ERROR, "Could not find a suitable format in %s", __PRETTY_FUNCTION__);
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
// Determine the frame size.
frame_size_ = audio::utils::ComputeFrameSize(static_cast<uint16_t>(format_v1.number_of_channels),
sample_format);
if (!frame_size_) {
zxlogf(ERROR, "Failed to compute frame size (ch %hu fmt 0x%08x)", format_v1.number_of_channels,
sample_format);
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
// Looks like we are going ahead with this format change. Tear down any
// exiting ring buffer interface before proceeding.
{
fbl::AutoLock channel_lock(&channel_lock_);
if (rb_channel_ != nullptr) {
DeactivateRingBufferChannel(rb_channel_.get());
ZX_DEBUG_ASSERT(rb_channel_ == nullptr);
}
}
audio_stream_cmd_set_format_req_t req = {};
req.frames_per_second = format_v1.frame_rate;
req.sample_format = sample_format;
req.channels = static_cast<uint16_t>(format_v1.number_of_channels);
req.channels_to_use_bitmask = format_v1.channels_to_use_bitmask;
// Actually attempt to change the format.
auto result = ChangeFormat(req);
if (result != ZX_OK) {
zxlogf(ERROR, "Could not ChangeFormat in %s", __PRETTY_FUNCTION__);
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
{
fbl::AutoLock channel_lock(&channel_lock_);
rb_channel_ = Channel::Create<Channel>();
fidl::OnUnboundFn<audio_fidl::RingBuffer::Interface> on_unbound =
[this](audio_fidl::RingBuffer::Interface*, fidl::UnbindInfo, zx::channel) {
ScopedToken t(domain_token());
fbl::AutoLock channel_lock(&channel_lock_);
this->DeactivateRingBufferChannel(rb_channel_.get());
};
fidl::BindServer<audio_fidl::RingBuffer::Interface>(dispatcher(), std::move(ring_buffer), this,
std::move(on_unbound));
}
}
void SimpleAudioStream::WatchGainState(StreamChannel* channel,
StreamChannel::WatchGainStateCompleter::Sync& completer) {
ZX_ASSERT(!channel->gain_completer_);
channel->gain_completer_ = completer.ToAsync();
ScopedToken t(domain_token());
// Reply is delayed if there is no change since the last reported gain state.
// TODO(andresoportus): Create a type with default <=> in C++20, or just defined ==.
if (channel->last_reported_gain_state_.cur_gain != cur_gain_state_.cur_gain ||
channel->last_reported_gain_state_.cur_mute != cur_gain_state_.cur_mute ||
channel->last_reported_gain_state_.cur_agc != cur_gain_state_.cur_agc ||
channel->last_reported_gain_state_.can_mute != cur_gain_state_.can_mute ||
channel->last_reported_gain_state_.can_agc != cur_gain_state_.can_agc ||
channel->last_reported_gain_state_.min_gain != cur_gain_state_.min_gain ||
channel->last_reported_gain_state_.max_gain != cur_gain_state_.max_gain ||
channel->last_reported_gain_state_.gain_step != cur_gain_state_.gain_step) {
auto builder = audio_fidl::GainState::UnownedBuilder();
fidl::aligned<bool> mute = cur_gain_state_.cur_mute;
fidl::aligned<bool> agc = cur_gain_state_.cur_agc;
fidl::aligned<float> gain = cur_gain_state_.cur_gain;
if (cur_gain_state_.can_mute) {
builder.set_muted(fidl::unowned_ptr(&mute));
}
if (cur_gain_state_.can_agc) {
builder.set_agc_enabled(fidl::unowned_ptr(&agc));
}
builder.set_gain_db(fidl::unowned_ptr(&gain));
channel->last_reported_gain_state_ = cur_gain_state_;
channel->gain_completer_->Reply(builder.build());
channel->gain_completer_.reset();
}
}
void SimpleAudioStream::WatchPlugState(StreamChannel* channel,
StreamChannel::WatchPlugStateCompleter::Sync& completer) {
ZX_ASSERT(!channel->plug_completer_);
channel->plug_completer_ = completer.ToAsync();
ScopedToken t(domain_token());
fidl::aligned<bool> plugged = pd_flags_ & AUDIO_PDNF_PLUGGED;
// Reply is delayed if there is no change since the last reported plugged state.
if (channel->last_reported_plugged_state_ == StreamChannel::Plugged::kNotReported ||
(channel->last_reported_plugged_state_ == StreamChannel::Plugged::kPlugged) != plugged) {
auto builder = audio_fidl::PlugState::UnownedBuilder();
builder.set_plugged(fidl::unowned_ptr(&plugged));
fidl::aligned<zx_time_t> plug_time = plug_time_;
builder.set_plug_state_time(fidl::unowned_ptr(&plug_time));
channel->last_reported_plugged_state_ =
plugged ? StreamChannel::Plugged::kPlugged : StreamChannel::Plugged::kUnplugged;
channel->plug_completer_->Reply(builder.build());
channel->plug_completer_.reset();
}
}
void SimpleAudioStream::WatchClockRecoveryPositionInfo(
WatchClockRecoveryPositionInfoCompleter::Sync& completer) {
fbl::AutoLock position_lock(&position_lock_);
position_completer_ = completer.ToAsync();
}
void SimpleAudioStream::SetGain(audio_fidl::GainState target_state,
StreamChannel::SetGainCompleter::Sync& completer) {
ScopedToken t(domain_token());
audio_stream_cmd_set_gain_req_t req = {};
// Sanity check the request before passing it along
if (target_state.has_muted() && target_state.muted() && !cur_gain_state_.can_mute) {
zxlogf(ERROR, "Can't mute\n");
return;
}
if (target_state.has_agc_enabled() && target_state.agc_enabled() && !cur_gain_state_.can_agc) {
zxlogf(ERROR, "Can't enable AGC\n");
return;
}
if (target_state.has_gain_db() && ((target_state.gain_db() < cur_gain_state_.min_gain) ||
(target_state.gain_db() > cur_gain_state_.max_gain))) {
zxlogf(ERROR, "Can't set gain outside valid range\n");
return;
}
if (target_state.has_muted()) {
req.flags |= AUDIO_SGF_MUTE_VALID;
if (target_state.muted()) {
req.flags |= AUDIO_SGF_MUTE;
}
}
if (target_state.has_agc_enabled()) {
req.flags |= AUDIO_SGF_AGC_VALID;
if (target_state.agc_enabled()) {
req.flags |= AUDIO_SGF_AGC;
}
}
if (target_state.has_gain_db()) {
req.flags |= AUDIO_SGF_GAIN_VALID;
req.gain = target_state.gain_db();
}
auto status = SetGain(req);
if (status != ZX_OK) {
zxlogf(ERROR, "Could not set gain state %d\n", status);
return;
}
fbl::AutoLock channel_lock(&channel_lock_);
for (auto& channel : stream_channels_) {
if (channel.gain_completer_) {
channel.gain_completer_->Reply(std::move(target_state));
channel.gain_completer_.reset();
}
}
}
void SimpleAudioStream::GetProperties(
audio_fidl::StreamConfig::Interface::GetPropertiesCompleter::Sync& completer) {
ScopedToken t(domain_token());
auto builder = audio_fidl::StreamProperties::UnownedBuilder();
fidl::Array<uint8_t, audio_fidl::UNIQUE_ID_SIZE> unique_id = {};
for (size_t i = 0; i < audio_fidl::UNIQUE_ID_SIZE; ++i) {
unique_id.data_[i] = unique_id_.data[i];
}
fidl::aligned<fidl::Array<uint8_t, audio_fidl::UNIQUE_ID_SIZE>> aligned_unique_id = unique_id;
builder.set_unique_id(fidl::unowned_ptr(&aligned_unique_id));
fidl::aligned<bool> input = is_input();
builder.set_is_input(fidl::unowned_ptr(&input));
fidl::aligned<bool> mute = cur_gain_state_.can_mute;
builder.set_can_mute(fidl::unowned_ptr(&mute));
fidl::aligned<bool> agc = cur_gain_state_.can_agc;
builder.set_can_agc(fidl::unowned_ptr(&agc));
fidl::aligned<float> min_gain = cur_gain_state_.min_gain;
builder.set_min_gain_db(fidl::unowned_ptr(&min_gain));
fidl::aligned<float> max_gain = cur_gain_state_.max_gain;
builder.set_max_gain_db(fidl::unowned_ptr(&max_gain));
fidl::aligned<float> gain_step = cur_gain_state_.gain_step;
builder.set_gain_step_db(fidl::unowned_ptr(&gain_step));
fidl::StringView product(prod_name_, strlen(prod_name_));
builder.set_product(fidl::unowned_ptr(&product));
fidl::StringView manufacturer(mfr_name_, strlen(mfr_name_));
builder.set_manufacturer(fidl::unowned_ptr(&manufacturer));
fidl::aligned<uint32_t> domain = clock_domain_;
builder.set_clock_domain(fidl::unowned_ptr(&domain));
fidl::aligned<audio_fidl::PlugDetectCapabilities> cap;
if (pd_flags_ & AUDIO_PDNF_CAN_NOTIFY) {
cap = audio_fidl::PlugDetectCapabilities::CAN_ASYNC_NOTIFY;
builder.set_plug_detect_capabilities(fidl::unowned_ptr(&cap));
} else if (pd_flags_ & AUDIO_PDNF_HARDWIRED) {
cap = audio_fidl::PlugDetectCapabilities::HARDWIRED;
builder.set_plug_detect_capabilities(fidl::unowned_ptr(&cap));
}
completer.Reply(builder.build());
}
void SimpleAudioStream::GetSupportedFormats(
audio_fidl::StreamConfig::Interface::GetSupportedFormatsCompleter::Sync& completer) {
ScopedToken t(domain_token());
// Build formats compatible with FIDL from a vector of audio_stream_format_range_t.
// Needs to be alive until the reply is sent.
struct FidlCompatibleFormats {
fbl::Vector<uint8_t> number_of_channels;
fbl::Vector<audio_fidl::SampleFormat> sample_formats;
fbl::Vector<uint32_t> frame_rates;
fbl::Vector<uint8_t> valid_bits_per_sample;
fbl::Vector<uint8_t> bytes_per_sample;
};
fbl::Vector<FidlCompatibleFormats> fidl_compatible_formats;
for (auto& i : supported_formats_) {
audio_fidl::SampleFormat sample_format = audio_fidl::SampleFormat::PCM_SIGNED;
ZX_ASSERT(!(i.sample_formats & AUDIO_SAMPLE_FORMAT_BITSTREAM));
ZX_ASSERT(!(i.sample_formats & AUDIO_SAMPLE_FORMAT_FLAG_INVERT_ENDIAN));
if (i.sample_formats & AUDIO_SAMPLE_FORMAT_FLAG_UNSIGNED) {
sample_format = audio_fidl::SampleFormat::PCM_UNSIGNED;
}
auto noflag_format =
static_cast<audio_sample_format_t>(i.sample_formats & ~AUDIO_SAMPLE_FORMAT_FLAG_MASK);
auto sizes = audio::utils::GetSampleSizes(noflag_format);
ZX_ASSERT(sizes.valid_bits_per_sample != 0);
ZX_ASSERT(sizes.bytes_per_sample != 0);
if (noflag_format == AUDIO_SAMPLE_FORMAT_32BIT_FLOAT) {
sample_format = audio_fidl::SampleFormat::PCM_FLOAT;
}
fbl::Vector<uint32_t> rates;
// Ignore flags if min and max are equal.
if (i.min_frames_per_second == i.max_frames_per_second) {
rates.push_back(i.min_frames_per_second);
} else {
ZX_ASSERT(!(i.flags & ASF_RANGE_FLAG_FPS_CONTINUOUS));
audio::utils::FrameRateEnumerator enumerator(i);
for (uint32_t rate : enumerator) {
rates.push_back(rate);
}
}
fbl::Vector<uint8_t> number_of_channels;
for (uint8_t j = i.min_channels; j <= i.max_channels; ++j) {
number_of_channels.push_back(j);
}
fidl_compatible_formats.push_back({
std::move(number_of_channels),
{sample_format},
std::move(rates),
{sizes.valid_bits_per_sample},
{sizes.bytes_per_sample},
});
}
// Get FIDL PcmSupportedFormats from FIDL compatible vectors.
// Needs to be alive until the reply is sent.
fbl::Vector<audio_fidl::PcmSupportedFormats> fidl_pcm_formats;
for (auto& i : fidl_compatible_formats) {
audio_fidl::PcmSupportedFormats formats;
formats.number_of_channels = ::fidl::VectorView<uint8_t>(
fidl::unowned_ptr(i.number_of_channels.data()), i.number_of_channels.size());
formats.sample_formats = ::fidl::VectorView<audio_fidl::SampleFormat>(
fidl::unowned_ptr(i.sample_formats.data()), i.sample_formats.size());
formats.frame_rates =
::fidl::VectorView<uint32_t>(fidl::unowned_ptr(i.frame_rates.data()), i.frame_rates.size());
formats.bytes_per_sample = ::fidl::VectorView<uint8_t>(
fidl::unowned_ptr(i.bytes_per_sample.data()), i.bytes_per_sample.size());
formats.valid_bits_per_sample = ::fidl::VectorView<uint8_t>(
fidl::unowned_ptr(i.valid_bits_per_sample.data()), i.valid_bits_per_sample.size());
fidl_pcm_formats.push_back(std::move(formats));
}
// Get builders from PcmSupportedFormats tables.
// Needs to be alive until the reply is sent.
fbl::Vector<audio_fidl::SupportedFormats::UnownedBuilder> fidl_builders;
for (auto& i : fidl_pcm_formats) {
auto builder = audio_fidl::SupportedFormats::UnownedBuilder();
builder.set_pcm_supported_formats(fidl::unowned_ptr(&i));
fidl_builders.push_back(std::move(builder));
}
// Build FIDL SupportedFormats from PcmSupportedFormats's builders.
// Needs to be alive until the reply is sent.
fbl::Vector<audio_fidl::SupportedFormats> fidl_formats;
for (auto& i : fidl_builders) {
fidl_formats.push_back(i.build());
}
completer.Reply(::fidl::VectorView<audio_fidl::SupportedFormats>(
fidl::unowned_ptr(fidl_formats.data()), fidl_formats.size()));
}
// Ring Buffer GetProperties.
void SimpleAudioStream::GetProperties(GetPropertiesCompleter::Sync& completer) {
ScopedToken t(domain_token());
auto builder = audio_fidl::RingBufferProperties::UnownedBuilder();
fidl::aligned<uint32_t> fifo_depth = fifo_depth_;
builder.set_fifo_depth(fidl::unowned_ptr(&fifo_depth));
fidl::aligned<int64_t> delay = static_cast<int64_t>(external_delay_nsec_);
builder.set_external_delay(fidl::unowned_ptr(&delay));
fidl::aligned<bool> flush = true;
builder.set_needs_cache_flush_or_invalidate(fidl::unowned_ptr(&flush));
completer.Reply(builder.build());
}
void SimpleAudioStream::GetVmo(uint32_t min_frames, uint32_t notifications_per_ring,
GetVmoCompleter::Sync& completer) {
ScopedToken t(domain_token());
if (rb_started_) {
completer.ReplyError(audio_fidl::GetVmoError::INTERNAL_ERROR);
return;
}
uint32_t num_ring_buffer_frames = 0;
audio_proto::RingBufGetBufferReq req = {};
req.min_ring_buffer_frames = min_frames;
req.notifications_per_ring = notifications_per_ring;
zx::vmo buffer;
auto status = GetBuffer(req, &num_ring_buffer_frames, &buffer);
if (status != ZX_OK) {
expected_notifications_per_ring_.store(0);
completer.ReplyError(audio_fidl::GetVmoError::INTERNAL_ERROR);
return;
}
expected_notifications_per_ring_.store(req.notifications_per_ring);
rb_fetched_ = true;
completer.ReplySuccess(num_ring_buffer_frames, std::move(buffer));
}
void SimpleAudioStream::Start(StartCompleter::Sync& completer) {
ScopedToken t(domain_token());
uint64_t start_time = 0;
if (rb_started_ || !rb_fetched_) {
zxlogf(ERROR, "Could not start %s\n", __PRETTY_FUNCTION__);
completer.Reply(start_time);
return;
}
auto result = Start(&start_time);
if (result == ZX_OK) {
rb_started_ = true;
}
completer.Reply(start_time);
}
void SimpleAudioStream::Stop(StopCompleter::Sync& completer) {
ScopedToken t(domain_token());
if (!rb_started_) {
zxlogf(ERROR, "Could not stop %s\n", __PRETTY_FUNCTION__);
completer.Reply();
return;
}
auto result = Stop();
if (result == ZX_OK) {
rb_started_ = false;
}
completer.Reply();
}
} // namespace audio