blob: efb8e07f576075ac6ac51d84e4229d78fd3690e1 [file] [log] [blame]
// Copyright 2017 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.
#ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_DRIVER_H_
#define SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_DRIVER_H_
#include <fuchsia/media/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/async/cpp/wait.h>
#include <lib/zx/channel.h>
#include <lib/zx/clock.h>
#include <lib/zx/vmo.h>
#include <zircon/device/audio.h>
#include <mutex>
#include <string>
#include "src/media/audio/audio_core/audio_device.h"
#include "src/media/audio/audio_core/audio_device_settings.h"
#include "src/media/audio/audio_core/channel_attributes.h"
#include "src/media/audio/audio_core/ring_buffer.h"
#include "src/media/audio/audio_core/utils.h"
namespace media::audio {
class AudioOutput;
struct HwGainState {
// TODO(johngro): when driver interfaces move to FIDL, just change this to match the fidl
// structure returned from a GetGain request by the driver.
bool cur_mute;
bool cur_agc;
float cur_gain;
bool can_mute;
bool can_agc;
float min_gain;
float max_gain;
float gain_step;
};
class AudioDriver {
public:
// Timeout values are chosen to be generous while still providing some guard-rails against
// hardware errors. Correctly functioning hardware and drivers should never result in any
// timeouts.
static constexpr zx::duration kDefaultShortCmdTimeout = zx::sec(2);
static constexpr zx::duration kDefaultLongCmdTimeout = zx::sec(4);
enum class State {
Uninitialized,
MissingDriverInfo,
Unconfigured,
Configuring_SettingFormat,
Configuring_GettingFifoDepth,
Configuring_GettingRingBuffer,
Configured,
Starting,
Started,
Stopping,
Shutdown,
};
explicit AudioDriver(AudioDevice* owner);
using DriverTimeoutHandler = fit::function<void(zx::duration)>;
AudioDriver(AudioDevice* owner, DriverTimeoutHandler timeout_handler);
virtual ~AudioDriver() = default;
zx_status_t Init(zx::channel stream_channel);
void Cleanup();
std::optional<Format> GetFormat() const;
bool plugged() const {
std::lock_guard<std::mutex> lock(plugged_lock_);
return plugged_;
}
zx::time plug_time() const {
std::lock_guard<std::mutex> lock(plugged_lock_);
return plug_time_;
}
// Methods which need to be called from the owner's execution domain. If there was a good way to
// use the static lock analysis to ensure this, I would do so, but unfortunately the compiler is
// unable to figure out that the owner calling these methods is always the same as owner_.
State state() const { return state_; }
zx::time ref_start_time() const { return ref_start_time_; }
zx::duration external_delay() const { return external_delay_; }
uint32_t fifo_depth_frames() const { return fifo_depth_frames_; }
zx::duration fifo_depth_duration() const { return fifo_depth_duration_; }
zx_koid_t stream_channel_koid() const { return stream_channel_koid_; }
const HwGainState& hw_gain_state() const { return hw_gain_state_; }
uint32_t clock_domain() const { return clock_domain_; }
virtual const TimelineFunction& ref_time_to_frac_presentation_frame() const {
return ref_time_to_frac_presentation_frame_;
}
virtual const TimelineFunction& ref_time_to_frac_safe_read_or_write_frame() const {
return ref_time_to_frac_safe_read_or_write_frame_;
}
// The following properties are only safe to access after the driver is beyond the
// MissingDriverInfo state. After that state, these members must be treated as immutable, and the
// driver class may no longer change them.
const audio_stream_unique_id_t& persistent_unique_id() const { return persistent_unique_id_; }
const std::string& manufacturer_name() const { return manufacturer_name_; }
const std::string& product_name() const { return product_name_; }
zx_status_t GetDriverInfo();
zx_status_t Configure(const Format& format, zx::duration min_ring_buffer_duration);
zx_status_t Start();
zx_status_t Stop();
zx_status_t SetPlugDetectEnabled(bool enabled);
zx_status_t SetGain(const AudioDeviceSettings::GainState& gain_state,
audio_set_gain_flags_t set_flags);
zx_status_t SelectBestFormat(uint32_t* frames_per_second_inout, uint32_t* channels_inout,
fuchsia::media::AudioSampleFormat* sample_format_inout);
// Accessors for the ring buffer pointer and the current output clock transformation.
//
// Note: Only AudioDriver writes to these, and only when in our owner's mixing execution
// domain. It is safe for our owner to read these objects, but only when operating in the mixing
// domain. Unfortunately, it is not practical to use the static thread safety annotation to prove
// that we are accessing these variable from the mixing domain. Instead, we...
//
// 1) Make these methods private.
// 2) Make the AudioDevice class (our owner) a friend.
// 3) Expose protected accessors in AudioDevice which demand that we execute in the mix domain.
//
// This should be a strong enough guarantee to warrant disabling the thread safety analysis here.
const std::shared_ptr<ReadableRingBuffer>& readable_ring_buffer() const
FXL_NO_THREAD_SAFETY_ANALYSIS {
return readable_ring_buffer_;
}
const std::shared_ptr<WritableRingBuffer>& writable_ring_buffer() const
FXL_NO_THREAD_SAFETY_ANALYSIS {
return writable_ring_buffer_;
}
AudioClock& reference_clock() { return *audio_clock_; }
zx::duration turn_on_delay() { return turn_on_delay_; }
std::vector<ChannelAttributes> channel_config() { return configured_channel_config_; }
zx_status_t SetActiveChannels(uint64_t chan_bit_mask);
private:
static bool ValidatePcmSupportedFormats(
std::vector<fuchsia::hardware::audio::PcmSupportedFormats>& formats, bool is_input);
static constexpr uint32_t kDriverInfoHasUniqueId = (1u << 0);
static constexpr uint32_t kDriverInfoHasMfrStr = (1u << 1);
static constexpr uint32_t kDriverInfoHasProdStr = (1u << 2);
static constexpr uint32_t kDriverInfoHasGainState = (1u << 3);
static constexpr uint32_t kDriverInfoHasFormats = (1u << 4);
static constexpr uint32_t kDriverInfoHasClockDomain = (1u << 5);
static constexpr uint32_t kDriverInfoHasAll = kDriverInfoHasUniqueId | kDriverInfoHasMfrStr |
kDriverInfoHasProdStr | kDriverInfoHasGainState |
kDriverInfoHasFormats | kDriverInfoHasClockDomain;
void SetUpClocks();
void ClockRecoveryUpdate(fuchsia::hardware::audio::RingBufferPositionInfo info);
zx_status_t SetGain(const AudioDeviceSettings::GainState& gain_state);
// Transition to the Shutdown state and begin the process of shutting down.
void ShutdownSelf(const char* debug_reason = nullptr, zx_status_t debug_status = ZX_OK)
FXL_EXCLUSIVE_LOCKS_REQUIRED(owner_->mix_domain().token());
// Evaluate each currently pending timeout. Program the command timeout timer appropriately.
void SetupCommandTimeout() FXL_EXCLUSIVE_LOCKS_REQUIRED(owner_->mix_domain().token());
// Update internal plug state bookkeeping and report up to our owner (if enabled).
void ReportPlugStateChange(bool plugged, zx::time plug_time)
FXL_EXCLUSIVE_LOCKS_REQUIRED(owner_->mix_domain().token());
// Handle a new piece of driver info being fetched.
zx_status_t OnDriverInfoFetched(uint32_t info)
FXL_EXCLUSIVE_LOCKS_REQUIRED(owner_->mix_domain().token());
// Simple accessors
bool operational() const FXL_EXCLUSIVE_LOCKS_REQUIRED(owner_->mix_domain().token()) {
return (state_ != State::Uninitialized) && (state_ != State::Shutdown);
}
bool fetching_driver_info() const FXL_EXCLUSIVE_LOCKS_REQUIRED(owner_->mix_domain().token()) {
return fetch_driver_info_deadline_ != zx::time::infinite();
}
void DriverCommandTimedOut() FXL_EXCLUSIVE_LOCKS_REQUIRED(owner_->mix_domain().token());
void RequestNextPlugStateChange() FXL_EXCLUSIVE_LOCKS_REQUIRED(owner_->mix_domain().token());
void RequestNextClockRecoveryUpdate() FXL_EXCLUSIVE_LOCKS_REQUIRED(owner_->mix_domain().token());
AudioDevice* const owner_;
DriverTimeoutHandler timeout_handler_;
State state_ = State::Uninitialized;
async::TaskClosure cmd_timeout_ FXL_GUARDED_BY(owner_->mix_domain().token());
zx_koid_t stream_channel_koid_ = ZX_KOID_INVALID;
zx::time fetch_driver_info_deadline_ = zx::time::infinite();
uint32_t fetched_driver_info_ FXL_GUARDED_BY(owner_->mix_domain().token()) = 0;
// State fetched at driver startup time.
audio_stream_unique_id_t persistent_unique_id_ = {0};
std::string manufacturer_name_;
std::string product_name_;
HwGainState hw_gain_state_;
std::vector<audio_stream_format_range_t> format_ranges_;
// Configuration state.
zx::time mono_start_time_;
zx::time ref_start_time_;
zx::duration external_delay_;
zx::duration min_ring_buffer_duration_;
uint32_t fifo_depth_frames_;
zx::duration fifo_depth_duration_;
zx::duration turn_on_delay_ = zx::nsec(0);
zx::time configuration_deadline_ = zx::time::infinite();
// A stashed copy of current format, queryable by destinations (outputs or AudioCapturers) when
// determining which mixer to use.
mutable std::mutex configured_format_lock_;
std::optional<Format> configured_format_ FXL_GUARDED_BY(configured_format_lock_);
std::vector<ChannelAttributes> configured_channel_config_;
// Ring buffer state. Details are lock-protected and changes tracked with generation counter,
// allowing AudioCapturer clients to snapshot ring-buffer state during mix/resample operations.
mutable std::mutex ring_buffer_state_lock_;
std::shared_ptr<ReadableRingBuffer> readable_ring_buffer_ FXL_GUARDED_BY(ring_buffer_state_lock_);
std::shared_ptr<WritableRingBuffer> writable_ring_buffer_ FXL_GUARDED_BY(ring_buffer_state_lock_);
// The timeline function which maps from either capture time (Input) or presentation time
// (Output) at speaker/microphone on the audio device's ref clock, to stream's subframe position.
//
// IOW - given a stream's frame number, use the inverse of this function to map to a time on
// device ref clock that the frame [was captured / will be presented].
fbl::RefPtr<VersionedTimelineFunction> versioned_ref_time_to_frac_presentation_frame_;
// Useful timeline functions which are computed after streaming starts. See
// the comments for the accessors in audio_device.h for detailed descriptions.
TimelineFunction ref_time_to_frac_presentation_frame_
FXL_GUARDED_BY(owner_->mix_domain().token());
TimelineFunction ref_time_to_frac_safe_read_or_write_frame_
FXL_GUARDED_BY(owner_->mix_domain().token());
TimelineRate frac_frames_per_byte_ FXL_GUARDED_BY(owner_->mix_domain().token());
mutable std::mutex plugged_lock_;
bool plugged_ FXL_GUARDED_BY(plugged_lock_) = false;
zx::time plug_time_ FXL_GUARDED_BY(plugged_lock_);
zx::time driver_last_timeout_ = zx::time::infinite();
// Plug detection state.
bool pd_hardwired_ = false;
std::vector<fuchsia::hardware::audio::PcmSupportedFormats> formats_;
// FIDL interface pointers.
fidl::InterfacePtr<fuchsia::hardware::audio::StreamConfig> stream_config_fidl_;
fidl::InterfacePtr<fuchsia::hardware::audio::RingBuffer> ring_buffer_fidl_;
uint32_t clock_domain_ = AudioClock::kMonotonicDomain;
std::unique_ptr<AudioClock> audio_clock_;
std::unique_ptr<AudioClock> recovered_clock_;
// Counter of received position notifications since START.
uint64_t position_notification_count_ FXL_GUARDED_BY(owner_->mix_domain().token()) = 0;
uint64_t ring_buffer_size_bytes_ FXL_GUARDED_BY(owner_->mix_domain().token());
uint64_t running_pos_bytes_ FXL_GUARDED_BY(owner_->mix_domain().token());
// If we get an error from ring_buffer_fidl->SetActiveChannels(), then we won't call it again
zx_status_t set_active_channels_err_ = ZX_OK;
};
} // namespace media::audio
#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_DRIVER_H_