1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#pragma once 6 7#include <fbl/intrusive_double_list.h> 8#include <fbl/vector.h> 9#include <zircon/device/audio.h> 10#include <zircon/hw/usb-audio.h> 11 12#include "usb-audio.h" 13#include "usb-audio-descriptors.h" 14 15namespace audio { 16namespace usb { 17 18class UsbAudioDevice; 19class AudioPath; 20 21class UsbAudioStreamInterface : 22 public fbl::DoublyLinkedListable<fbl::unique_ptr<UsbAudioStreamInterface>> { 23 public: 24 // A small helper struct which maps from a Fuchsia format range to the 25 // alternate interface ID which supports that range. 26 struct FormatMapEntry { 27 const audio_stream_format_range_t range_; 28 29 // The alternatate interface ID, endpoint address, and maximum request 30 // size which need to be used when configuring the stream interface to 31 // use the format described by range_. 32 const uint8_t alt_id_; 33 const uint8_t ep_addr_; 34 const uint16_t max_req_size_; 35 36 FormatMapEntry(const audio_stream_format_range_t& range, 37 uint8_t alt_id, 38 uint8_t ep_addr, 39 uint16_t max_req_size) 40 : range_(range), 41 alt_id_(alt_id), 42 ep_addr_(ep_addr), 43 max_req_size_(max_req_size) {} 44 }; 45 46 // Note that UsbAudioStreamInterfaces are entirely owned by UsbAudioDevice 47 // instances. The stream interface needs to hold a pointer to its parent, 48 // so it is critically important that the owning parent is certain that the 49 // the stream interface (and all of its children) have been properly shut 50 // down before exiting. At all times, the lifetime of the stream interface 51 // needs to be a subset of the lifetime of the device parent. 52 // 53 // Note, the iterator passed to the create method *must* be pointing at a 54 // valid interface header with class == audio and subclass == streaming 55 // interface. The interface ID encountered in this first header will become 56 // the interface ID of this StreamInterface object. 57 static fbl::unique_ptr<UsbAudioStreamInterface> 58 Create(UsbAudioDevice* parent, DescriptorListMemory::Iterator* iter); 59 60 // Called to add a new alternate streaming interface to this StreamInterface 61 // object. The iterator must be pointing at a valid audio stream interface 62 // descriptor which shares a an IID with this object. 63 zx_status_t AddInterface(DescriptorListMemory::Iterator* iter); 64 65 // Called after all of the interface descriptors have been discovered and 66 // added to this stream interface to allow the stream interface a chance to 67 // build its list of format ranges and the alternate interface ID which 68 // support them. 69 zx_status_t BuildFormatMap(); 70 71 // Called from the UsbAudioStream to lookup the index of a the format which 72 // matches the user's request. Note, this does not actually cause the 73 // interface to switch to this format. Use ActivateFormat, passing the 74 // index retrieved from there, to achieve that. 75 zx_status_t LookupFormat(uint32_t frames_per_second, 76 uint16_t channels, 77 audio_sample_format_t sample_format, 78 size_t* out_format_ndx); 79 80 // Called from the UsbAudioStream to activate the chosen format interface 81 // and to configure the specific frame rate for that interface. 82 zx_status_t ActivateFormat(size_t ndx, uint32_t frames_per_second); 83 84 // Called from the UsbAudioStream to activate the alternate idle interface 85 // (if any). Will return ZX_ERR_NOT_SUPPORTED if there is no idle 86 // interface. 87 zx_status_t ActivateIdleFormat(); 88 89 // Called at the end of device probing to link a discovered audio path to 90 // this stream interface. 91 void LinkPath(fbl::unique_ptr<AudioPath> path); 92 93 uint8_t iid() const { return iid_; } 94 uint16_t max_req_size() const { return max_req_size_; } 95 const fbl::unique_ptr<AudioPath>& path() { return path_; } 96 const fbl::Vector<FormatMapEntry>& formats() { return format_map_; } 97 98 // Properties shared by all formats of this stream interface. 99 uint8_t term_link() const { return term_link_; } 100 uint8_t ep_addr() const { return ep_addr_; } 101 uint8_t ep_attr() const { return ep_attr_; } 102 103 Direction direction() const { 104 return (ep_addr() & USB_ENDPOINT_DIR_MASK) 105 ? Direction::Input 106 : Direction::Output; 107 } 108 109 EndpointSyncType ep_sync_type() const { 110 return static_cast<EndpointSyncType>(ep_attr() & USB_ENDPOINT_SYNCHRONIZATION_MASK); 111 } 112 113 // Accessor for debug logging. 114 const char* log_prefix() const; 115 116 private: 117 friend class fbl::unique_ptr<UsbAudioStreamInterface>; 118 119 // An internal helper class which contains all of the information we need to 120 // support an alternate interface setting which supports a given format. 121 class Format : public fbl::DoublyLinkedListable<fbl::unique_ptr<Format>> { 122 public: 123 Format(const UsbAudioStreamInterface* parent, 124 fbl::RefPtr<DescriptorListMemory> desc_list, 125 const usb_interface_descriptor_t* interface_hdr, 126 const usb_audio_as_header_desc* class_hdr) 127 : parent_(parent), 128 desc_list_(fbl::move(desc_list)), 129 interface_hdr_(interface_hdr), 130 class_hdr_(class_hdr) {} 131 132 const char* log_prefix() const { return parent_->log_prefix(); } 133 uint8_t iid() const { return interface_hdr_->bInterfaceNumber; } 134 uint8_t alt_id() const { return interface_hdr_->bAlternateSetting; } 135 uint8_t term_link() const { return class_hdr_->bTerminalLink; } 136 uint16_t format_tag() const { return class_hdr_->wFormatTag; } 137 uint8_t ep_addr() const { return ep_desc_->bEndpointAddress; } 138 uint8_t ep_attr() const { return ep_desc_->bmAttributes; } 139 uint16_t max_req_size() const { return ep_desc_->wMaxPacketSize; } 140 uint8_t frame_rate_cnt() const { return fmt_desc_->bSamFreqType; } 141 uint8_t ch_count() const { return fmt_desc_->bNrChannels; } 142 uint8_t bit_resolution() const { return fmt_desc_->bBitResolution; } 143 uint8_t subframe_bytes() const { return fmt_desc_->bSubFrameSize; } 144 145 // Min/Max continuous frame rates. Valid *only* after initilaize has 146 // been successfully called, and *only* if frame_rate_cnt() == 0. 147 uint32_t min_cont_frame_rate() const { 148 ZX_DEBUG_ASSERT(frame_rate_cnt() == 0); 149 return UnpackFrameRate(fmt_desc_->tSamFreq[0]); 150 } 151 152 uint32_t max_cont_frame_rate() const { 153 ZX_DEBUG_ASSERT(frame_rate_cnt() == 0); 154 return UnpackFrameRate(fmt_desc_->tSamFreq[1]); 155 } 156 157 // Fetch discrete frame rate #ndx. Valid *only* after initilaize has 158 // been successfully called, and *only* if ndx < frame_rate_cnt() 159 uint32_t frame_rate(uint8_t ndx) const { 160 ZX_DEBUG_ASSERT(ndx < frame_rate_cnt()); 161 return UnpackFrameRate(fmt_desc_->tSamFreq[ndx]); 162 } 163 164 zx_status_t Init(DescriptorListMemory::Iterator* iter); 165 166 private: 167 friend class fbl::unique_ptr<Format>; 168 ~Format() = default; 169 170 // Packing format described in section 2.2.5 of USB Device Class 171 // Definition for Audio Data Formats. 172 static inline uint32_t UnpackFrameRate(const usb_audio_as_samp_freq& rate) { 173 return (static_cast<uint32_t>(rate.freq[2]) << 16) | 174 (static_cast<uint32_t>(rate.freq[1]) << 8) | 175 static_cast<uint32_t>(rate.freq[0]); 176 } 177 178 // Determined at construction time 179 const UsbAudioStreamInterface* parent_; 180 const fbl::RefPtr<DescriptorListMemory> desc_list_; 181 const usb_interface_descriptor_t* const interface_hdr_; 182 const usb_audio_as_header_desc* const class_hdr_; 183 184 // Determined at initializtion time 185 const usb_audio_as_format_type_i_desc* fmt_desc_ = nullptr; 186 const usb_endpoint_descriptor_t* ep_desc_ = nullptr; 187 const usb_audio_as_isoch_ep_desc* class_ep_desc_ = nullptr; 188 }; 189 190 UsbAudioStreamInterface(UsbAudioDevice* parent, 191 fbl::RefPtr<DescriptorListMemory> desc_list, 192 uint8_t iid) 193 : parent_(*parent), 194 iid_(iid), 195 desc_list_(fbl::move(desc_list)) { 196 ZX_DEBUG_ASSERT(parent != nullptr); 197 } 198 ~UsbAudioStreamInterface() = default; 199 200 // The reference to our parent. Note, because of this unmanaged reference, 201 // it is critically important that the surrounding code ensure that we never 202 // outlive our parent device. 203 UsbAudioDevice& parent_; 204 205 // The unique interface ID for this group of alternate interface descriptions. 206 const uint8_t iid_; 207 208 // Cached, unmanaged pointers to our interface and class descriptors. The 209 // memory which backs these descriptors is kept alive by the top level 210 // desc_list_ reference. 211 // 212 // TODO(johngro) : this desc_list_ memory is contained in our parent 213 // UsbAudioDevice. Since we have already commited to having a lifetime 214 // which is strictly <= the lifetime of our parent, we should probably just 215 // access the descriptor memory using our parent instead of holding our own 216 // reference to it. 217 const fbl::RefPtr<DescriptorListMemory> desc_list_; 218 219 // A pointer to an "idle" interface; IOW an interface which defines no 220 // endpoints. While not all audio streaming interfaces have one of these, 221 // many seem to. In theory, this allows a stream interface to save 222 // isochronos bandwidth by selecting an alternatate interface which requires 223 // no isoch bandwidth allocation when the device is idle. 224 const usb_interface_descriptor_t* idle_hdr_ = nullptr; 225 226 // The terminal link ID which is shared by all of the valid formats we have 227 // discovered. 228 uint8_t term_link_ = 0xFF; 229 230 // The endpoint address and attributes which are shared by all of the valid 231 // formats we have discovered. 232 uint8_t ep_addr_ = 0xFF; 233 uint8_t ep_attr_ = 0x0; 234 235 // The largest maximum request size computed across all of our discovered 236 // endpoints. 237 uint16_t max_req_size_ = 0; 238 239 // A list of the formats (generic descrptors followed by a class specific 240 // interface descriptor) we have discovered. 241 fbl::DoublyLinkedList<fbl::unique_ptr<Format>> formats_; 242 243 // The path through the control interface's terminal/unit graph that this 244 // streaming interface is linked to. 245 fbl::unique_ptr<AudioPath> path_; 246 247 // A vector which contains the mappings from Fuchsia format ranges to the 248 // alternate interface ID of the interface which supports that format range. 249 fbl::Vector<FormatMapEntry> format_map_; 250}; 251 252} // namespace usb 253} // namespace audio 254 255