// 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. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #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; class UsbAudioStream : public UsbAudioStreamBase, public AudioStreamProtocol, public fbl::RefCounted, public fbl::DoublyLinkedListable> { public: static fbl::RefPtr Create(UsbAudioDevice* parent, fbl::unique_ptr ifc); zx_status_t Bind(); const char* log_prefix() const { return log_prefix_; } // DDK device implementation void DdkUnbind(); void DdkRelease(); zx_status_t DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, void* out_buf, size_t out_len, size_t* out_actual); private: friend class fbl::RefPtr; enum class RingBufferState { STOPPED, STOPPING, STOPPING_AFTER_UNPLUG, STARTING, STARTED, }; UsbAudioStream(UsbAudioDevice* parent, fbl::unique_ptr ifc, fbl::RefPtr default_domain); virtual ~UsbAudioStream(); void ComputePersistentUniqueId(); void ReleaseRingBufferLocked() __TA_REQUIRES(lock_); // Thunks for dispatching stream channel events. zx_status_t ProcessStreamChannel(dispatcher::Channel* channel, bool privileged); void DeactivateStreamChannel(const dispatcher::Channel* channel); zx_status_t OnGetStreamFormatsLocked(dispatcher::Channel* channel, const audio_proto::StreamGetFmtsReq& req) __TA_REQUIRES(lock_); zx_status_t OnSetStreamFormatLocked(dispatcher::Channel* channel, const audio_proto::StreamSetFmtReq& req, bool privileged) __TA_REQUIRES(lock_); zx_status_t OnGetGainLocked(dispatcher::Channel* channel, const audio_proto::GetGainReq& req) __TA_REQUIRES(lock_); zx_status_t OnSetGainLocked(dispatcher::Channel* channel, const audio_proto::SetGainReq& req) __TA_REQUIRES(lock_); zx_status_t OnPlugDetectLocked(dispatcher::Channel* channel, const audio_proto::PlugDetectReq& req) __TA_REQUIRES(lock_); zx_status_t OnGetUniqueIdLocked(dispatcher::Channel* channel, const audio_proto::GetUniqueIdReq& req) __TA_REQUIRES(lock_); zx_status_t OnGetStringLocked(dispatcher::Channel* channel, const audio_proto::GetStringReq& req) __TA_REQUIRES(lock_); // Thunks for dispatching ring buffer channel events. zx_status_t ProcessRingBufferChannel(dispatcher::Channel* channel); void DeactivateRingBufferChannel(const dispatcher::Channel* channel); // Stream command handlers // Ring buffer command handlers zx_status_t OnGetFifoDepthLocked(dispatcher::Channel* channel, const audio_proto::RingBufGetFifoDepthReq& req) __TA_REQUIRES(lock_); zx_status_t OnGetBufferLocked(dispatcher::Channel* channel, const audio_proto::RingBufGetBufferReq& req) __TA_REQUIRES(lock_); zx_status_t OnStartLocked(dispatcher::Channel* channel, const audio_proto::RingBufStartReq& req) __TA_REQUIRES(lock_); zx_status_t OnStopLocked(dispatcher::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_); UsbAudioDevice& parent_; const fbl::unique_ptr 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 stream_channel_ __TA_GUARDED(lock_); fbl::RefPtr rb_channel_ __TA_GUARDED(lock_); fbl::RefPtr default_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; }; } // namespace usb } // namespace audio