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/intrusive_wavl_tree.h>
9 #include <fbl/unique_ptr.h>
10 #include <zircon/hw/usb/audio.h>
11 
12 #include "usb-audio-descriptors.h"
13 #include "usb-audio-path.h"
14 #include "usb-audio-units.h"
15 
16 namespace audio {
17 namespace usb {
18 
19 class UsbAudioDevice;
20 
21 class UsbAudioControlInterface {
22   public:
23     // Note that UsbAudioControlInterfaces are entirely owned by UsbAudioDevice
24     // instances.  The control interface needs to hold a pointer to its parent,
25     // so it is critically important that the owning parent is certain that the
26     // control interface (and all of its children) have been properly shut down
27     // before exiting.  At all times, the lifetime of the control interface
28     // needs to be a subset of the lifetime of the device parent.
29     static fbl::unique_ptr<UsbAudioControlInterface> Create(UsbAudioDevice* parent);
30     zx_status_t Initialize(DescriptorListMemory::Iterator* iter);
31 
32     const char* log_prefix() const;
33 
34     // Extract the AudioPath whose streaming terminal interface link ID matches
35     // the users request, if any.  Otherwise, simply return nullptr.
ExtractPath(uint8_t term_link,Direction direction)36     fbl::unique_ptr<AudioPath> ExtractPath(uint8_t term_link, Direction direction) {
37         return paths_.erase_if(
38             [term_link, direction](const AudioPath& path) -> bool {
39                 return (path.stream_terminal().id() == term_link) &&
40                        (path.direction() == direction);
41             });
42     }
43 
44 
45   private:
46     friend class fbl::unique_ptr<UsbAudioControlInterface>;
47     using UnitMap = fbl::WAVLTree<uint32_t, fbl::RefPtr<AudioUnit>>;
48 
49     UsbAudioControlInterface(UsbAudioDevice* parent);
50     ~UsbAudioControlInterface();
51 
52     // A recursive method used to find interesting audio paths in the
53     // unit/terminal graph.  See the comment at the end of the Init
54     // implementation for details about the algorithm used to find these paths.
55     fbl::unique_ptr<AudioPath> TracePath(const OutputTerminal& out_term,
56                                          const UnitMap::iterator& current,
57                                          uint32_t level = 0);
58 
59     // The reference to our parent.  Note, because of this unmanaged reference,
60     // it is critically important that the surrounding code ensure that we never
61     // outlive our parent device.
62     UsbAudioDevice& parent_;
63 
64     // Cached, unmanaged pointers to our interface and class descriptors.  The
65     // memory which backs these descriptors is kept alive by the top level
66     // desc_list_ reference.
67     //
68     // TODO(johngro) : this desc_list_ memory is contained in our parent
69     // UsbAudioDevice.  Since we have already committed to having a lifetime
70     // which is strictly <= the lifetime of our parent, we should probably just
71     // access the descriptor memory using our parent instead of holding our own
72     // reference to it.
73     fbl::RefPtr<DescriptorListMemory> desc_list_;
74     const usb_interface_descriptor_t* interface_hdr_ = nullptr;
75     const usb_audio_ac_header_desc* class_hdr_ = nullptr;
76 
77     // All unit/terminal IDs for a given Audio Control Interface
78     //
79     // TODO(johngro): Strictly speaking, we don't really need to hold onto this.
80     // If we wanted, we could just keep this as a local variable used during
81     // Init and discard it afterwards.
82     UnitMap units_;
83 
84     // The complete set of valid paths we have discovered.
85     fbl::DoublyLinkedList<fbl::unique_ptr<AudioPath>> paths_;
86 };
87 
88 }  // namespace usb
89 }  // namespace audio
90