blob: 34dcb3e928dbbbaf2e1a3c67756abe67a756bc67 [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_DRIVERS_USB_AUDIO_USB_AUDIO_STREAM_H_
#define SRC_MEDIA_AUDIO_DRIVERS_USB_AUDIO_USB_AUDIO_STREAM_H_
#include <fuchsia/hardware/audio/llcpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/wait.h>
#include <lib/zx/profile.h>
#include <lib/zx/vmo.h>
#include <zircon/listnode.h>
#include <memory>
#include <audio-proto/audio-proto.h>
#include <ddktl/device-internal.h>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/mutex.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fbl/vector.h>
#include <usb/usb.h>
#include "debug-logging.h"
namespace audio {
namespace usb {
class UsbAudioDevice;
class UsbAudioStreamInterface;
struct AudioStreamProtocol : public ddk::internal::base_protocol {
explicit AudioStreamProtocol(bool is_input) {
ddk_proto_id_ = is_input ? ZX_PROTOCOL_AUDIO_INPUT : ZX_PROTOCOL_AUDIO_OUTPUT;
}
bool is_input() const { return (ddk_proto_id_ == ZX_PROTOCOL_AUDIO_INPUT); }
};
class UsbAudioStream;
using UsbAudioStreamBase = ddk::Device<UsbAudioStream, ddk::Messageable, ddk::Unbindable>;
class UsbAudioStream : public UsbAudioStreamBase,
public AudioStreamProtocol,
public fbl::RefCounted<UsbAudioStream>,
public fbl::DoublyLinkedListable<fbl::RefPtr<UsbAudioStream>>,
public ::llcpp::fuchsia::hardware::audio::Device::Interface {
public:
class Channel : public fbl::DoublyLinkedListable<fbl::RefPtr<Channel>>,
public fbl::RefCounted<Channel> {
public:
template <typename T = Channel, typename... ConstructorSignature>
static fbl::RefPtr<T> Create(ConstructorSignature&&... args) {
fbl::AllocChecker ac;
auto ptr = fbl::AdoptRef(new (&ac) T(std::forward<ConstructorSignature>(args)...));
if (!ac.check()) {
return nullptr;
}
return ptr;
}
void SetHandler(async::Wait::Handler handler) { wait_.set_handler(std::move(handler)); }
zx_status_t BeginWait(async_dispatcher_t* dispatcher) { return wait_.Begin(dispatcher); }
zx_status_t Write(const void* buffer, uint32_t length) {
return channel_.write(0, buffer, length, nullptr, 0);
}
zx_status_t Write(const void* buffer, uint32_t length, zx::handle&& handle) {
zx_handle_t h = handle.release();
return channel_.write(0, buffer, length, &h, 1);
}
zx_status_t Read(void* buffer, uint32_t length, uint32_t* out_length) {
return channel_.read(0, buffer, nullptr, length, 0, out_length, nullptr);
}
protected:
explicit Channel(zx::channel channel) : channel_(std::move(channel)) {
wait_.set_object(channel_.get());
wait_.set_trigger(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED);
}
~Channel() = default; // Deactivates (automatically cancels the wait from its RAII semantics).
private:
friend class fbl::RefPtr<Channel>;
zx::channel channel_;
async::Wait wait_;
};
static fbl::RefPtr<UsbAudioStream> Create(UsbAudioDevice* parent,
std::unique_ptr<UsbAudioStreamInterface> ifc);
zx_status_t Bind();
void StreamChannelSignalled(async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal,
Channel* channel, bool priviledged);
void RingBufferChannelSignalled(async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal,
Channel* channel);
const char* log_prefix() const { return log_prefix_; }
// DDK device implementation
void DdkUnbind(ddk::UnbindTxn txn);
void DdkRelease();
zx_status_t DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn) {
DdkTransaction transaction(txn);
llcpp::fuchsia::hardware::audio::Device::Dispatch(this, msg, &transaction);
return transaction.Status();
}
private:
friend class fbl::RefPtr<UsbAudioStream>;
enum class RingBufferState {
STOPPED,
STOPPING,
STOPPING_AFTER_UNPLUG,
STARTING,
STARTED,
};
UsbAudioStream(UsbAudioDevice* parent, std::unique_ptr<UsbAudioStreamInterface> ifc);
virtual ~UsbAudioStream();
void ComputePersistentUniqueId();
void ReleaseRingBufferLocked() __TA_REQUIRES(lock_);
// Device FIDL implementation
void GetChannel(GetChannelCompleter::Sync& completer) override;
// Thunks for dispatching stream channel events.
zx_status_t ProcessStreamChannel(Channel* channel, bool privileged);
void DeactivateStreamChannel(const Channel* channel);
zx_status_t OnGetStreamFormatsLocked(Channel* channel, const audio_proto::StreamGetFmtsReq& req)
__TA_REQUIRES(lock_);
zx_status_t OnSetStreamFormatLocked(Channel* channel, const audio_proto::StreamSetFmtReq& req,
bool privileged) __TA_REQUIRES(lock_);
zx_status_t OnGetGainLocked(Channel* channel, const audio_proto::GetGainReq& req)
__TA_REQUIRES(lock_);
zx_status_t OnSetGainLocked(Channel* channel, const audio_proto::SetGainReq& req)
__TA_REQUIRES(lock_);
zx_status_t OnPlugDetectLocked(Channel* channel, const audio_proto::PlugDetectReq& req)
__TA_REQUIRES(lock_);
zx_status_t OnGetUniqueIdLocked(Channel* channel, const audio_proto::GetUniqueIdReq& req)
__TA_REQUIRES(lock_);
zx_status_t OnGetStringLocked(Channel* channel, const audio_proto::GetStringReq& req)
__TA_REQUIRES(lock_);
zx_status_t OnGetClockDomainLocked(Channel* channel, const audio_proto::GetClockDomainReq& req)
__TA_REQUIRES(lock_);
// Thunks for dispatching ring buffer channel events.
zx_status_t ProcessRingBufferChannel(Channel* channel);
void DeactivateRingBufferChannel(const Channel* channel);
// Stream command handlers
// Ring buffer command handlers
zx_status_t OnGetFifoDepthLocked(Channel* channel, const audio_proto::RingBufGetFifoDepthReq& req)
__TA_REQUIRES(lock_);
zx_status_t OnGetBufferLocked(Channel* channel, const audio_proto::RingBufGetBufferReq& req)
__TA_REQUIRES(lock_);
zx_status_t OnStartLocked(Channel* channel, const audio_proto::RingBufStartReq& req)
__TA_REQUIRES(lock_);
zx_status_t OnStopLocked(Channel* channel, const audio_proto::RingBufStopReq& req)
__TA_REQUIRES(lock_);
void RequestComplete(usb_request_t* req);
void QueueRequestLocked() __TA_REQUIRES(req_lock_);
void CompleteRequestLocked(usb_request_t* req) __TA_REQUIRES(req_lock_);
static void RequestCompleteCallback(void* ctx, usb_request_t* request);
UsbAudioDevice& parent_;
const std::unique_ptr<UsbAudioStreamInterface> ifc_;
char log_prefix_[LOG_PREFIX_STORAGE] = {0};
audio_stream_unique_id_t persistent_unique_id_;
fbl::Mutex lock_;
fbl::Mutex req_lock_ __TA_ACQUIRED_AFTER(lock_);
// Dispatcher framework state
fbl::RefPtr<Channel> stream_channel_ __TA_GUARDED(lock_);
fbl::RefPtr<Channel> rb_channel_ __TA_GUARDED(lock_);
int32_t clock_domain_;
size_t selected_format_ndx_;
uint32_t selected_frame_rate_;
uint32_t frame_size_;
uint32_t iso_packet_rate_;
uint32_t bytes_per_packet_;
uint32_t fifo_bytes_;
uint32_t fractional_bpp_inc_;
uint32_t fractional_bpp_acc_ __TA_GUARDED(req_lock_);
uint32_t ring_buffer_offset_ __TA_GUARDED(req_lock_);
uint64_t usb_frame_num_ __TA_GUARDED(req_lock_);
uint32_t bytes_per_notification_ = 0;
uint32_t notification_acc_ __TA_GUARDED(req_lock_);
zx::vmo ring_buffer_vmo_;
void* ring_buffer_virt_ = nullptr;
uint32_t ring_buffer_size_ = 0;
uint32_t ring_buffer_pos_ __TA_GUARDED(req_lock_);
volatile RingBufferState ring_buffer_state_ __TA_GUARDED(req_lock_) = RingBufferState::STOPPED;
union {
audio_proto::RingBufStopResp stop;
audio_proto::RingBufStartResp start;
} pending_job_resp_ __TA_GUARDED(req_lock_);
list_node_t free_req_ __TA_GUARDED(req_lock_);
uint32_t free_req_cnt_ __TA_GUARDED(req_lock_);
uint32_t allocated_req_cnt_;
const zx_time_t create_time_;
// TODO(johngro) : See MG-940. eliminate this ASAP
bool req_complete_prio_bumped_ = false;
zx::profile profile_handle_;
async::Loop loop_;
};
} // namespace usb
} // namespace audio
#endif // SRC_MEDIA_AUDIO_DRIVERS_USB_AUDIO_USB_AUDIO_STREAM_H_