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 #include <fbl/auto_call.h>
6 
7 #include <utility>
8 
9 #include "debug-logging.h"
10 #include "usb-audio-control-interface.h"
11 #include "usb-audio-device.h"
12 #include "usb-audio-units.h"
13 
14 namespace audio {
15 namespace usb {
16 
17 // We use our parent's log prefix
log_prefix() const18 const char* UsbAudioControlInterface::log_prefix() const {
19     return parent_.log_prefix();
20 }
21 
UsbAudioControlInterface(UsbAudioDevice * parent)22 UsbAudioControlInterface::UsbAudioControlInterface(UsbAudioDevice* parent)
23     : parent_(*parent) {
24     ZX_DEBUG_ASSERT(parent != nullptr);
25 }
26 
~UsbAudioControlInterface()27 UsbAudioControlInterface::~UsbAudioControlInterface() {
28 }
29 
Create(UsbAudioDevice * parent)30 fbl::unique_ptr<UsbAudioControlInterface> UsbAudioControlInterface::Create(UsbAudioDevice* parent) {
31     if (parent == nullptr) {
32         GLOBAL_LOG(ERROR, "null parent passed to %s\n", __PRETTY_FUNCTION__);
33         return nullptr;
34     }
35 
36     fbl::AllocChecker ac;
37     fbl::unique_ptr<UsbAudioControlInterface> ret(new (&ac) UsbAudioControlInterface(parent));
38     if (ac.check()) {
39         return ret;
40     }
41 
42     return nullptr;
43 }
44 
Initialize(DescriptorListMemory::Iterator * iter)45 zx_status_t UsbAudioControlInterface::Initialize(DescriptorListMemory::Iterator* iter) {
46     ZX_DEBUG_ASSERT(iter != nullptr);
47     ZX_DEBUG_ASSERT(iter->desc_list() != nullptr);
48 
49     // It is an error to attempt to initialize this class twice.
50     if (desc_list_ != nullptr) {
51         LOG(ERROR, "Attempted to initialize control interface twice\n");
52         return ZX_ERR_BAD_STATE;
53     }
54 
55     desc_list_ = iter->desc_list();
56     interface_hdr_ = iter->hdr_as<usb_interface_descriptor_t>();
57 
58     // These should already have been checked before Initialize was called.
59     ZX_DEBUG_ASSERT(interface_hdr_ != nullptr);
60     ZX_DEBUG_ASSERT(interface_hdr_->bInterfaceClass == USB_CLASS_AUDIO);
61     ZX_DEBUG_ASSERT(interface_hdr_->bInterfaceSubClass == USB_SUBCLASS_AUDIO_CONTROL);
62 
63     // Parse all of the descriptors which belong to this audio control
64     // interface.  As soon as we find something which does not belong to the
65     // interface, break out of the parse loop, leaving the iterator pointing at
66     // the next descriptor (if any).  Then try to make sense of the descriptors
67     // we did find.
68     while (iter->Next()) {
69         {
70             auto hdr = iter->hdr();
71             if (!hdr || (hdr->bDescriptorType != USB_AUDIO_CS_INTERFACE)) {
72                 break;
73             }
74         }
75 
76         auto hdr = iter->hdr_as<usb_audio_desc_header>();
77         if (!hdr) {
78             LOG(WARN, "Badly formed audio control descriptor header @ offset %zu\n",
79                 iter->offset());
80             continue;
81         }
82 
83         if (hdr->bDescriptorSubtype == USB_AUDIO_AC_HEADER) {
84             if (class_hdr_ == nullptr) {
85                 class_hdr_ = iter->hdr_as<usb_audio_ac_header_desc>();
86                 if (class_hdr_ == nullptr) {
87                     LOG(WARN, "Badly formed audio control class specific header @ offset %zu\n",
88                         iter->offset());
89                 }
90             } else {
91                 LOG(WARN, "Duplicate audio control class specific header @ offset %zu\n",
92                     iter->offset());
93             }
94 
95             continue;
96         }
97 
98         auto unit = AudioUnit::Create(*iter, interface_hdr_->bInterfaceNumber);
99         if (unit == nullptr) {
100             LOG(WARN, "Failed to create audio Terminal/Unit (type %u) @ offset %zu\n",
101                 hdr->bDescriptorSubtype, iter->offset());
102         } else {
103             // Add our new unit to the collection we are building up.  There
104             // should be no collision; all unit IDs are supposed to be
105             // unique within a given control interface.  If we encounter a
106             // collision, log a warning and move on (eg, just try to do the
107             // best we can).
108             uint32_t id = unit->id();
109             if (!units_.insert_or_find(std::move(unit))) {
110                 LOG(WARN, "Collision when attempting to add unit id %u; skipping this unit\n", id);
111             }
112         }
113     }
114 
115     // Next, give each Unit/Terminal a chance to probe any state they will need
116     // to operate which will require performing actual USB transactions.
117     for (auto& unit : units_) {
118         zx_status_t res = unit.Probe(parent_.usb_proto());
119         if (res != ZX_OK) {
120             LOG(ERROR, "Failed to probe %s (id %u) during initialization!\n",
121                 unit.type_name(), unit.id());
122             return res;
123         }
124     }
125 
126     // OK - now that we have our set of descriptors, attempt to find the audio
127     // paths through this graph that we intend to publish.  The algorithm used
128     // here is not particularly sophisticated.  Basically, we are going to start
129     // at each output terminal in the set and attempt to trace our way back to
130     // an input terminal that forms a path from host to pin (or vice versa).
131     // Pin-to-pin or host-to-host paths are ignored, although if we someday want
132     // to recognize sidetone paths, we should probably pay some attention to the
133     // pin to pin paths.
134     //
135     // We explore the graph using a depth first recursive search using a state
136     // bit stored in the terminal/unit classes to avoid cycles.  Since the unit
137     // IDs used by terminal/units are 8-bits, we can only recurse an absolute
138     // maximum of 256 times, which should be safe from stack overflow for the
139     // class of hardware this driver is intended for.
140     //
141     // Once any valid path from output to input has been found, we stop the
142     // search, even if there may be another path to consider.  For most simple
143     // devices out there, this should be sufficient, however as time goes on we
144     // may discover more complicated devices that will require us to revisit
145     // this algorithm and make it a bit smarter.  Failing that, a custom driver
146     // would be needed for these more complicated hypothetical devices.
147     for (auto iter = units_.begin(); iter.IsValid(); ++iter) {
148         // We are only interested in output terminals.  Skip all of the rest.
149         if (iter->type() != AudioUnit::Type::OutputTerminal) {
150             continue;
151         }
152 
153         // Do the search.  If it succeed, we will get a reference to an
154         // AudioPath object back.
155         LOG(TRACE, "Beginning trace for Output Terminal id %u\n", iter->id());
156         auto path = TracePath(static_cast<const OutputTerminal&>(*iter), iter);
157         if (path != nullptr) {
158             LOG(TRACE, "Found valid path!\n");
159 
160             zx_status_t status = path->Setup(parent_.usb_proto());
161             if (status != ZX_OK) {
162                 LOG(TRACE, "Failed to setup path! (status %d)\n", status);
163             } else {
164                 paths_.push_back(std::move(path));
165             }
166         } else {
167             LOG(TRACE, "No valid path found\n");
168         }
169     }
170 
171     // Now that we have found all of our valid paths, go over our list of
172     // discovered units and mute any volume controls in feature units which are
173     // not currently being used by any audio paths.
174     for (auto& unit : units_) {
175         if ((unit.type() == AudioUnit::Type::FeatureUnit) && !unit.in_use()) {
176             auto& feature_unit = static_cast<FeatureUnit&>(unit);
177             feature_unit.SetMute(parent_.usb_proto(), true);
178         }
179 
180         // TODO(johngro) : If we encounter un-used mixer nodes, we should set
181         // all of their inputs to maximum dB down in an attempt to effectively
182         // mute them.
183     }
184 
185     return ZX_OK;
186 }
187 
TracePath(const OutputTerminal & out_term,const UnitMap::iterator & current,uint32_t level)188 fbl::unique_ptr<AudioPath> UsbAudioControlInterface::TracePath(const OutputTerminal& out_term,
189                                                                const UnitMap::iterator& current,
190                                                                uint32_t level) {
191     // Flag the current node as having been visited and setup a cleanup task to
192     // clear the flag as we unwind.
193     auto cleanup = fbl::MakeAutoCall([&current]() { current->visited() = false; });
194     ZX_DEBUG_ASSERT(!current->visited());
195     current->visited() = true;
196     LOG(TRACE, "Visiting unit id %u, type %s\n", current->id(), current->type_name());
197 
198     // If we have reached an input terminal, then check to see if it is of the
199     // proper type.  If so, create a new path object and start to unwind the
200     // stack, stashing the references to the unit which define the path in the
201     // process.  Otherwise, this is a dead end.  Just return null and keep
202     // looking.
203     if (current->type() == AudioUnit::Type::InputTerminal) {
204         // We have found a valid path of one of these terminals is a USB stream
205         // terminal, while the other terminal is anything which is not a USB
206         // terminal (stream or otherwise).
207         const auto& in_term = static_cast<const InputTerminal&>(*current);
208         if ((out_term.is_stream_terminal() && !in_term.is_usb_terminal()) ||
209            (!out_term.is_stream_terminal() &&  in_term.is_usb_terminal())) {
210             auto ret = AudioPath::Create(level + 1);
211             if (ret != nullptr) {
212                 ret->AddUnit(level, current.CopyPointer());
213             }
214             return ret;
215         }
216 
217         LOG(TRACE, "Skipping incompatible input terminal (in type 0x%04hx, out type 0x%04hx)\n",
218             in_term.terminal_type(), out_term.terminal_type());
219         return nullptr;
220     }
221 
222     for (uint32_t i = 0; i < current->source_count(); ++i) {
223         uint32_t source_id = current->source_id(i);
224         auto next = units_.find(source_id);
225         if (!next.IsValid()) {
226             LOG(WARN, "Can't find upstream unit id %u while tracing from unit id %u.\n",
227                 source_id, current->id());
228             continue;
229         }
230 
231         if (next->visited()) {
232             LOG(TRACE, "Skipping already visited unit id %u while tracing from unit id %u\n",
233                 source_id, current->id());
234             continue;
235         }
236 
237         // Recurse down this path.  If it finds a valid path, stash ourselves in
238         // the path and unwind.
239         auto path = TracePath(out_term, next, level + 1);
240         if (path != nullptr) {
241             path->AddUnit(level, current.CopyPointer());
242             return path;
243         }
244     }
245 
246     return nullptr;
247 }
248 
249 }  // namespace usb
250 }  // namespace audio
251