blob: ae8b326a0bc8000d7fa95ef7f721ffee296cf5e4 [file] [log] [blame]
// Copyright 2024 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 "src/media/audio/services/device_registry/testing/fake_composite_ring_buffer.h"
#include <fidl/fuchsia.hardware.audio/cpp/fidl.h>
#include <fidl/fuchsia.hardware.audio/cpp/test_base.h>
#include <lib/fit/result.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/zx/clock.h>
#include <zircon/errors.h>
#include <cstddef>
#include <optional>
#include "src/media/audio/services/device_registry/basic_types.h"
#include "src/media/audio/services/device_registry/logging.h"
#include "src/media/audio/services/device_registry/testing/fake_composite.h"
namespace media_audio {
FakeCompositeRingBuffer::FakeCompositeRingBuffer(FakeComposite* parent, ElementId element_id,
fuchsia_hardware_audio::PcmFormat format,
size_t ring_buffer_allocated_size)
: TestBase(),
parent_(parent),
element_id_(element_id),
format_(std::move(format)),
bytes_per_frame_(format_.number_of_channels() * format_.bytes_per_sample()),
active_channels_bitmask_((1u << format_.number_of_channels()) - 1u),
active_channels_set_time_(zx::clock::get_monotonic()) {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
AllocateRingBuffer(element_id_, ring_buffer_allocated_size);
++count_;
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer) << "There are now " << count_ << " instances";
}
FakeCompositeRingBuffer::~FakeCompositeRingBuffer() {
--count_;
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer) << "There are now " << count_ << " instances";
}
void FakeCompositeRingBuffer::AllocateRingBuffer(ElementId element_id, size_t size) {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
FX_CHECK(!vmo_.is_valid()) << "Calling AllocateRingBuffer multiple times is not supported";
allocated_size_ = size;
fzl::VmoMapper mapper;
mapper.CreateAndMap(allocated_size_, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, nullptr, &vmo_);
}
void FakeCompositeRingBuffer::NotImplemented_(const std::string& name,
::fidl::CompleterBase& completer) {
ADR_WARN_METHOD() << name;
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void FakeCompositeRingBuffer::GetProperties(GetPropertiesCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
fuchsia_hardware_audio::RingBufferProperties props;
if (needs_cache_flush_or_invalidate_) {
props.needs_cache_flush_or_invalidate(*needs_cache_flush_or_invalidate_);
}
if (turn_on_delay_) {
props.turn_on_delay(turn_on_delay_->get());
}
if (driver_transfer_bytes_) {
props.driver_transfer_bytes(*driver_transfer_bytes_);
}
completer.Reply(props);
}
void FakeCompositeRingBuffer::GetVmo(GetVmoRequest& request, GetVmoCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
auto total_requested_size =
driver_transfer_bytes_.value_or(0) + request.min_frames() * bytes_per_frame_;
if (total_requested_size > allocated_size_) {
ADR_WARN_METHOD() << "Requested size " << total_requested_size << " exceeds allocated size "
<< allocated_size_;
completer.Reply(fit::error(fuchsia_hardware_audio::GetVmoError::kInvalidArgs));
return;
}
clock_recovery_notifications_per_ring_ = request.clock_recovery_notifications_per_ring();
requested_frames_ = (total_requested_size - 1) / bytes_per_frame_ + 1;
// Dup our ring buffer VMO to send over the channel.
zx::vmo out_vmo;
FX_CHECK(vmo_.duplicate(ZX_RIGHT_SAME_RIGHTS, &out_vmo) == ZX_OK);
completer.Reply(zx::ok(fuchsia_hardware_audio::RingBufferGetVmoResponse{{
.num_frames = requested_frames_,
.ring_buffer = std::move(out_vmo),
}}));
}
void FakeCompositeRingBuffer::Start(StartCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
if (!vmo_.is_valid() || started_) {
completer.Close(ZX_ERR_BAD_STATE);
return;
}
started_ = true;
mono_start_time_ = zx::clock::get_monotonic();
completer.Reply(mono_start_time_.get());
}
void FakeCompositeRingBuffer::Stop(StopCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
if (!vmo_.is_valid() || !started_) {
completer.Close(ZX_ERR_BAD_STATE);
return;
}
started_ = false;
mono_start_time_ = zx::time(0);
completer.Reply();
}
void FakeCompositeRingBuffer::SetActiveChannels(SetActiveChannelsRequest& request,
SetActiveChannelsCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
if (!supports_active_channels_) {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
return;
}
if (request.active_channels_bitmask() >= (1u << format_.number_of_channels())) {
completer.Reply(zx::error(ZX_ERR_INVALID_ARGS));
return;
}
if (active_channels_bitmask_ != request.active_channels_bitmask()) {
active_channels_bitmask_ = request.active_channels_bitmask();
active_channels_set_time_ = zx::clock::get_monotonic();
}
completer.Reply(zx::ok(active_channels_set_time_.get()));
}
void FakeCompositeRingBuffer::WatchDelayInfo(WatchDelayInfoCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
if (watch_delay_info_completer_.has_value()) {
completer.Close(ZX_ERR_BAD_STATE);
return;
}
watch_delay_info_completer_ = completer.ToAsync();
MaybeCompleteWatchDelayInfo();
}
void FakeCompositeRingBuffer::InjectDelayUpdate(std::optional<zx::duration> internal_delay,
std::optional<zx::duration> external_delay) {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
if (internal_delay.value_or(zx::nsec(0)) != internal_delay_.value_or(zx::nsec(0)) ||
external_delay.value_or(zx::nsec(0)) != external_delay_.value_or(zx::nsec(0))) {
delays_have_changed_ = true;
}
internal_delay_ = internal_delay;
external_delay_ = external_delay;
MaybeCompleteWatchDelayInfo();
}
void FakeCompositeRingBuffer::MaybeCompleteWatchDelayInfo() {
ADR_LOG_METHOD(kLogFakeCompositeRingBuffer);
if (delays_have_changed_ && watch_delay_info_completer_) {
delays_have_changed_ = false;
auto completer = std::move(*watch_delay_info_completer_);
watch_delay_info_completer_.reset();
fuchsia_hardware_audio::DelayInfo info;
if (internal_delay_) {
info.internal_delay(internal_delay_->get());
}
if (external_delay_) {
info.external_delay(external_delay_->get());
}
completer.Reply(std::move(info));
}
}
void FakeCompositeRingBuffer::WatchClockRecoveryPositionInfo(
WatchClockRecoveryPositionInfoCompleter::Sync& completer) {
NotImplemented_("WatchClockRecoveryPositionInfo", completer);
}
} // namespace media_audio