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