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/algorithm.h>
6 #include <limits>
7 #include <math.h>
8 #include <utility>
9 
10 #include "debug-logging.h"
11 #include "usb-audio-units.h"
12 
13 namespace audio {
14 namespace usb {
15 
16 // a small internal helper methods which handles a bunch of ugly casting for us.
17 template <typename T, typename U>
offset_ptr(const U * p,size_t offset)18 static inline const T* offset_ptr(const U* p, size_t offset) {
19     return ((offset + sizeof(T)) <= p->bLength)
20         ? reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(p) + offset)
21         : nullptr;
22 }
23 
type_name() const24 const char* AudioUnit::type_name() const {
25     switch (type()) {
26         case Type::InputTerminal:  return "InputTerminal";
27         case Type::OutputTerminal: return "OutputTerminal";
28         case Type::MixerUnit:      return "MixerUnit";
29         case Type::SelectorUnit:   return "SelectorUnit";
30         case Type::FeatureUnit:    return "FeatureUnit";
31         case Type::ProcessingUnit: return "ProcessingUnit";
32         case Type::ExtensionUnit:  return "ExtensionUnit";
33         default:                   return "<Unknown>";
34     }
35 }
36 
Create(const DescriptorListMemory::Iterator & iter,uint8_t iid)37 fbl::RefPtr<AudioUnit> AudioUnit::Create(const DescriptorListMemory::Iterator& iter, uint8_t iid) {
38     auto hdr = iter.hdr_as<usb_audio_desc_header>();
39 
40     // This should already have been verified by the code calling us.
41     ZX_DEBUG_ASSERT(hdr != nullptr);
42 
43     switch (hdr->bDescriptorSubtype) {
44         case USB_AUDIO_AC_INPUT_TERMINAL:   return InputTerminal::Create(iter, iid);
45         case USB_AUDIO_AC_OUTPUT_TERMINAL:  return OutputTerminal::Create(iter, iid);
46         case USB_AUDIO_AC_MIXER_UNIT:       return MixerUnit::Create(iter, iid);
47         case USB_AUDIO_AC_SELECTOR_UNIT:    return SelectorUnit::Create(iter, iid);
48         case USB_AUDIO_AC_FEATURE_UNIT:     return FeatureUnit::Create(iter, iid);
49         case USB_AUDIO_AC_PROCESSING_UNIT:  return ProcessingUnit::Create(iter, iid);
50         case USB_AUDIO_AC_EXTENSION_UNIT:   return ExtensionUnit::Create(iter, iid);
51         default:
52             GLOBAL_LOG(WARN, "Unrecognized audio control descriptor (type %u) @ offset %zu\n",
53                        hdr->bDescriptorSubtype, iter.offset());
54             return nullptr;
55     }
56 }
57 
CtrlReq(const usb_protocol_t & proto,uint8_t code,uint16_t val,uint16_t len,void * data)58 zx_status_t AudioUnit::CtrlReq(const usb_protocol_t& proto,
59                                uint8_t code, uint16_t val, uint16_t len, void* data) {
60     if (!len || (data == nullptr)) {
61         return ZX_ERR_INVALID_ARGS;
62     }
63 
64     // For audio class specific control codes, get control codes all have their MSB set.
65     uint8_t req_type = (code & 0x80)
66                      ? USB_DIR_IN  | USB_TYPE_CLASS | USB_RECIP_INTERFACE
67                      : USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
68 
69     // TODO(johngro) : See about fixing the use of const in the C API for the
70     // USB bus protocol.  There is no good reason why a usb_protocol structure
71     // should need to be mutable when performing operations such as usb_control.
72     auto proto_ptr = const_cast<usb_protocol_t*>(&proto);
73 
74     // TODO(johngro) : Do better than this if we can.
75     //
76     // None of these control transactions should every take any
77     // significant amount of time, and if the turn out to do so, then we really
78     // need to find a way to use the USB bus driver in an asynchronous fashion.
79     // Even 500 mSec is just *way* too long to ever block a driver thread, for
80     // pretty much any reason.  Right now, this timeout is here only for safety
81     // reasons; it would be better to timeout after a half of a second then to
82     // block the entire USB device forever.
83     //
84     // It is tempting to simply kill the driver/process if we ever timeout on
85     // one of these operations, but at time this code was written, that would
86     // kill the entire USB bus driver.  So, for now, we eat the timeout and rely
87     // on the code above us taking some action to shut this device down.
88     constexpr uint64_t kRelativeTimeout = ZX_MSEC(500);
89     size_t done = 0;
90     zx_status_t status;
91     if ((req_type & USB_DIR_MASK) == USB_DIR_OUT) {
92         status = usb_control_out(proto_ptr, req_type, code, val, index(),
93                              zx_deadline_after(kRelativeTimeout), data, len);
94         done = len;
95     } else {
96         status = usb_control_in(proto_ptr, req_type, code, val, index(),
97                              zx_deadline_after(kRelativeTimeout), data, len, &done);
98     }
99     if ((status == ZX_OK) && (done != len)) {
100         status = ZX_ERR_BUFFER_TOO_SMALL;
101     }
102 
103     if (status != ZX_OK) {
104         GLOBAL_LOG(WARN,
105                    "WARNING: Audio control request failed! Unit (%s:id %u), "
106                    "code 0x%02x val 0x%04hx, ndx 0x%04x [bytes expected %u, got %zu] (status %d)\n",
107                    type_name(), id(), code, val, index(), len, done, status);
108     }
109 
110     return status;
111 }
112 
Create(const DescriptorListMemory::Iterator & iter,uint8_t iid)113 fbl::RefPtr<InputTerminal> InputTerminal::Create(const DescriptorListMemory::Iterator& iter,
114                                                  uint8_t iid) {
115     auto hdr = iter.hdr_as<usb_audio_ac_input_terminal_desc>();
116 
117     if (hdr == nullptr) {
118         GLOBAL_LOG(WARN, "InputTerminal header appears invalid @ offset %zu\n", iter.offset());
119         return nullptr;
120     }
121 
122     // TODO(johngro): additional sanity checking and pre-processing goes here.
123 
124     fbl::AllocChecker ac;
125     auto ret = fbl::AdoptRef(new (&ac) InputTerminal(iter.desc_list(), hdr, iid));
126     return ac.check() ? ret : nullptr;
127 }
128 
Create(const DescriptorListMemory::Iterator & iter,uint8_t iid)129 fbl::RefPtr<OutputTerminal> OutputTerminal::Create(const DescriptorListMemory::Iterator& iter,
130                                                    uint8_t iid) {
131     auto hdr = iter.hdr_as<usb_audio_ac_output_terminal_desc>();
132 
133     if (hdr == nullptr) {
134         GLOBAL_LOG(WARN, "OutputTerminal header appears invalid @ offset %zu\n", iter.offset());
135         return nullptr;
136     }
137 
138     // TODO(johngro): additional sanity checking and pre-processing goes here.
139 
140     fbl::AllocChecker ac;
141     auto ret = fbl::AdoptRef(new (&ac) OutputTerminal(iter.desc_list(), hdr, iid));
142     return ac.check() ? ret : nullptr;
143 }
144 
Create(const DescriptorListMemory::Iterator & iter,uint8_t iid)145 fbl::RefPtr<MixerUnit> MixerUnit::Create(const DescriptorListMemory::Iterator& iter, uint8_t iid) {
146     // Find the size of each of the inlined variable length arrays in this
147     // structure, finding the locations of the constant headers in the process.
148     // If anything does not look right, complain and move on.
149     auto hdr0 = iter.hdr_as<usb_audio_ac_mixer_unit_desc_0>();
150     if (hdr0 != nullptr) {
151         size_t off = sizeof(*hdr0) + hdr0->bNrInPins;
152         auto hdr1 = offset_ptr<usb_audio_ac_mixer_unit_desc_1>(hdr0, off);
153         if (hdr1 != nullptr) {
154             // Determining the size of bmControls is a bit of a pain.  To do so,
155             // we need to know 'n', which is the sum the number of channels
156             // across all of the input pins, and 'm' (which should be
157             // hdr1->bNrChannels).  At this stage of parsing our unit/terminal
158             // graph, we may not have access to all of the sources which might
159             // feed into the calculation of 'n'.  Because of this, for now, just
160             // assume that the size of bmControls (in bytes) is equal to the
161             // space remaining in the descriptor, demanding that this be at
162             // least equal to a single byte (if it was zero, it means that we
163             // either have no input or no output channels, neither of which
164             // makes sense).
165             if (sizeof(usb_audio_ac_mixer_unit_desc_2) < hdr0->bLength) {
166                 size_t off2 = hdr0->bLength - sizeof(usb_audio_ac_mixer_unit_desc_2);
167                 if (off2 > off) {
168                     auto hdr2 = offset_ptr<usb_audio_ac_mixer_unit_desc_2>(hdr0, off2);
169                     ZX_DEBUG_ASSERT(hdr2 != nullptr);
170 
171                     // TODO(johngro): additional sanity checking and pre-processing goes here.
172                     fbl::AllocChecker ac;
173                     auto ret = fbl::AdoptRef(
174                             new (&ac) MixerUnit(iter.desc_list(), hdr0, hdr1, hdr2, iid));
175                     return ac.check() ? ret : nullptr;
176                 }
177             }
178         }
179     }
180 
181     GLOBAL_LOG(WARN, "MixerUnit header appears invalid @ offset %zu\n", iter.offset());
182     return nullptr;
183 }
184 
Create(const DescriptorListMemory::Iterator & iter,uint8_t iid)185 fbl::RefPtr<SelectorUnit> SelectorUnit::Create(const DescriptorListMemory::Iterator& iter,
186                                                uint8_t iid) {
187     // Find the size of each of the inlined variable length arrays in this
188     // structure, finding the locations of the constant headers in the process.
189     // If anything does not look right, complain and move on.
190     auto hdr0 = iter.hdr_as<usb_audio_ac_selector_unit_desc_0>();
191     if (hdr0 != nullptr) {
192         size_t off = sizeof(*hdr0) + hdr0->bNrInPins;
193         auto hdr1 = offset_ptr<usb_audio_ac_selector_unit_desc_1>(hdr0, off);
194         if (hdr1 != nullptr) {
195             // TODO(johngro): additional sanity checking and pre-processing goes here.
196             fbl::AllocChecker ac;
197             auto ret = fbl::AdoptRef(new (&ac) SelectorUnit(iter.desc_list(), hdr0, hdr1, iid));
198             return ac.check() ? ret : nullptr;
199         }
200     }
201 
202     GLOBAL_LOG(WARN, "SelectorUnit header appears invalid @ offset %zu\n", iter.offset());
203     return nullptr;
204 }
205 
Select(const usb_protocol_t & proto,uint8_t upstream_id)206 zx_status_t SelectorUnit::Select(const usb_protocol_t& proto, uint8_t upstream_id) {
207     // Section 5.2.2.3.3. defines the selector index as being 1s indexed, so
208     // zero is an easy to use "invalid" value.
209     uint8_t ndx = 0;
210 
211     // Find the appropriate index or return an error trying.
212     uint32_t cnt = source_count();
213     for (uint32_t i = 0; i < cnt; ++i) {
214         if (upstream_id == source_id(i)) {
215             ndx = static_cast<uint8_t>(i + 1);
216             break;
217         }
218     }
219 
220     if (!ndx) {
221         return ZX_ERR_INVALID_ARGS;
222     }
223 
224     // Now go ahead and set the value;
225     return CtrlReq(proto, USB_AUDIO_SET_CUR, 0, &ndx);
226 }
227 
Create(const DescriptorListMemory::Iterator & iter,uint8_t iid)228 fbl::RefPtr<FeatureUnit> FeatureUnit::Create(const DescriptorListMemory::Iterator& iter,
229                                              uint8_t iid) {
230     // Find the size of each of the inlined variable length arrays in this
231     // structure, finding the locations of the constant headers in the process.
232     // If anything does not look right, complain and move on.
233     auto hdr0 = iter.hdr_as<usb_audio_ac_feature_unit_desc_0>();
234     if (hdr0 != nullptr) {
235         // The exact expected size of the Controls bitmap depends on the number
236         // of channels feeding this feature unit.  This information is not
237         // contained in the feature unit itself, but instead exists upstream of
238         // the unit in first unit/terminal which contains a channel cluster
239         // element.  At this point in parsing, we have not discovered all of the
240         // units present in the audio control interface yet, so we cannot trace
241         // upstream to sanity check the size of this field.
242         //
243         // For now, we perform the most basic check we can by assuming that the
244         // size of the Controls bitmap must be...
245         //
246         // 1) Non-zero, and...
247         // 2) Divisible by bControlSize, which must also be non-zero.
248         //
249         // In the future, more stringent checks can be applied during Probe.
250         constexpr size_t kHdrOverhead = sizeof(*hdr0) + sizeof(usb_audio_ac_feature_unit_desc_1);
251         size_t ctrl_array_bytes = hdr0->bLength - kHdrOverhead;
252         if ((kHdrOverhead < hdr0->bLength) &&
253             (hdr0->bControlSize > 0) &&
254             (!(ctrl_array_bytes % hdr0->bControlSize))) {
255             // Allocate memory for our Features capability array.
256             fbl::AllocChecker ac;
257             size_t feat_len = ctrl_array_bytes / hdr0->bControlSize;
258             auto feat_mem = fbl::unique_ptr<Features[]>(new (&ac) Features[feat_len]);
259 
260             if (ac.check()) {
261                 // We just made sure that this fits, there should be no way for us
262                 // to have run out of data.
263                 size_t off = hdr0->bLength - sizeof(usb_audio_ac_feature_unit_desc_1);
264                 auto hdr1 = offset_ptr<usb_audio_ac_feature_unit_desc_1>(hdr0, off);
265                 ZX_DEBUG_ASSERT(hdr1 != nullptr);
266 
267                 auto ret = fbl::AdoptRef(new (&ac) FeatureUnit(iter.desc_list(),
268                                                                hdr0, hdr1,
269                                                                std::move(feat_mem), feat_len,
270                                                                iid));
271                 if (ac.check()) {
272                     return ret;
273                 }
274             }
275 
276             GLOBAL_LOG(WARN, "Out of memory attempting to allocate FeatureUnit @ offset %zu\n",
277                        iter.offset());
278             return nullptr;
279         }
280     }
281 
282     GLOBAL_LOG(WARN, "FeatureUnit header appears invalid @ offset %zu\n", iter.offset());
283     return nullptr;
284 }
285 
Probe(const usb_protocol_t & proto)286 zx_status_t FeatureUnit::Probe(const usb_protocol_t& proto) {
287     zx_status_t res;
288 
289     // Start by going over our channel feature bitmap and extracting the actual
290     // feature bits for each channel.  Right now, we demand that the size of
291     // each entry be (at most) a 32 bit integer.  The USB Audio 1.0 Spec only
292     // defines bits up to bit 9, so we really only understand how to handle up
293     // to there.  If we cannot fit each of the bitmap entries in a 32-bit
294     // integer, then the USB audio spec has come a long way and someone should
295     // come back here and update this driver.
296     ZX_DEBUG_ASSERT(feature_desc()->bControlSize != 0); // Create should have checked this already
297     if (feature_desc()->bControlSize > sizeof(uint32_t)) {
298         GLOBAL_LOG(WARN, "FeatureUnit id %u has unsupported bControlSize > %zu (%u)\n",
299                    id(), sizeof(uint32_t), feature_desc()->bControlSize);
300         return ZX_ERR_NOT_SUPPORTED;
301     }
302 
303     for (size_t i = 0; i < features_.size(); ++i) {
304         auto& f = features_[i];
305         f.supported_ = 0;
306         for (uint8_t j = 0; j <feature_desc()->bControlSize; ++j) {
307             uint32_t bits = feature_desc()->bmaControls[(i * feature_desc()->bControlSize) + j];
308             f.supported_ |= bits << (8 * j);
309         }
310     }
311 
312     // Now, go over our array of features and compute both the union and the
313     // intersection of the features for all of the individual channels.
314     uint32_t ch_feat_union = 0;
315     uint32_t ch_feat_intersection = 0;
316     if (features_.size() > 1) {
317         ch_feat_union = features_[1].supported_;
318         ch_feat_intersection = features_[1].supported_;
319         for (size_t i = 2; i < features_.size(); ++i) {
320             ch_feat_union |= features_[i].supported_;
321             ch_feat_intersection &= features_[i].supported_;
322 
323         }
324     }
325 
326     // Next check for a set of uniformity requirements.  In particular, there
327     // are three types of controls (mute, AGC, and volume/gain) that we want to
328     // enforce these guarantees for.  Specifically,
329     //
330     // 1) We can handle these controls at the master level, or the individual
331     //    channel level, but we don't really know what to do if the controls
332     //    exist at both levels.
333     // 2) If we are controlling these things at the individual control level, we
334     //    are doing so in a way which mimics a master control knob only.  So, if
335     //    we have these controls at the per-channel level, it is important that
336     //    they be they be identical for each of the individual channels.
337     constexpr uint32_t kUniformControls = USB_AUDIO_FU_BMA_MUTE
338                                         | USB_AUDIO_FU_BMA_VOLUME
339                                         | USB_AUDIO_FU_BMA_AUTOMATIC_GAIN;
340     ZX_DEBUG_ASSERT(features_.size() > 0); // Create should have checked this already
341     if (((features_[0].supported_ & ch_feat_union & kUniformControls) != 0) ||  // Check #1
342         ((ch_feat_union ^ ch_feat_intersection) & kUniformControls)) {          // Check #2
343         GLOBAL_LOG(WARN,
344                    "FeatureUnit id %u has unsupported non-uniform gain controls.  "
345                    "Master 0x%08x, Channel Union 0x%08x, Channel Intersection 0x%08x.\n",
346                    id(), features_[0].supported_, ch_feat_union, ch_feat_intersection);
347         return ZX_ERR_NOT_SUPPORTED;
348     }
349 
350     // Stash bitmaps of controls we care about for later.
351     master_feat_ = features_[0].supported_ & kUniformControls;
352     ch_feat_ = ch_feat_intersection & kUniformControls;
353 
354     // If this feature unit has volume control, fetch and sanity check the
355     // min/max/res of all of the channels.
356     if (has_vol()) {
357         // Go over each of the volume controls and cache the min/max/res values.
358         for (size_t i = 0; i < features_.size(); ++i) {
359             auto& f = features_[i];
360 
361             if (!f.has_vol()) {
362                 continue;
363             }
364 
365             uint8_t ch = static_cast<uint8_t>(i);
366 
367             res = FeatCtrlReq(proto, USB_AUDIO_GET_MIN, USB_AUDIO_VOLUME_CONTROL, ch, &f.vol_min_);
368             if (res != ZX_OK) {
369                 return res;
370             }
371 
372             res = FeatCtrlReq(proto, USB_AUDIO_GET_MAX, USB_AUDIO_VOLUME_CONTROL, ch, &f.vol_max_);
373             if (res != ZX_OK) {
374                 return res;
375             }
376 
377             res = FeatCtrlReq(proto, USB_AUDIO_GET_RES, USB_AUDIO_VOLUME_CONTROL, ch, &f.vol_res_);
378             if (res != ZX_OK) {
379                 return res;
380             }
381         }
382 
383         // If volume control is done at the per-channel level, make sure that all of
384         // the channels support the same range.  Otherwise, our volume control range
385         // is equal to the master channel's range.
386         if (features_[0].has_vol()) {
387             vol_min_ = features_[0].vol_min_;
388             vol_max_ = features_[0].vol_max_;
389             vol_res_ = features_[0].vol_res_;
390         } else {
391             vol_min_ = features_[1].vol_min_;
392             vol_max_ = features_[1].vol_max_;
393             vol_res_ = features_[1].vol_res_;
394             for (size_t i = 2; i < features_.size(); ++i) {
395                 if ((vol_min_ != features_[i].vol_min_) ||
396                     (vol_max_ != features_[i].vol_max_) ||
397                     (vol_res_ != features_[i].vol_res_)) {
398                     GLOBAL_LOG(WARN,
399                             "FeatureUnit id %u has unsupported non-uniform gain controls.  "
400                             "Channel %zu's gain range [%hd, %hd, %hd] does not match Channel 1's "
401                             "range [%hd, %hd, %hd]\n",
402                             id(), i,
403                             vol_min_, vol_max_, vol_res_,
404                             features_[i].vol_min_, features_[i].vol_max_, features_[i].vol_res_);
405                     return ZX_ERR_NOT_SUPPORTED;
406                 }
407             }
408         }
409 
410         if (vol_min_ > vol_max_) {
411             GLOBAL_LOG(WARN, "FeatureUnit id %u has invalid volume range [%hd, %hd]\n",
412                        id(), vol_min_, vol_max_);
413             return ZX_ERR_NOT_SUPPORTED;
414         }
415 
416         if (!vol_res_) {
417             GLOBAL_LOG(WARN, "FeatureUnit id %u has invalid volume res %hd\n", id(), vol_res_);
418             return ZX_ERR_NOT_SUPPORTED;
419         }
420 
421         // Fetch the current volume setting from the appropriate source, then
422         // make certain that all channels are set to the same if there is no
423         // master control knob.
424         bool master_control = (master_feat_ & USB_AUDIO_FU_BMA_VOLUME);
425         uint8_t ch = master_control ? 0 : 1;
426         res = FeatCtrlReq(proto, USB_AUDIO_GET_CUR, USB_AUDIO_VOLUME_CONTROL, ch, &vol_cur_);
427         if (res != ZX_OK) {
428             return res;
429         }
430 
431         if (!master_control) {
432             SetFeature(proto, USB_AUDIO_VOLUME_CONTROL, vol_cur_);
433         }
434     }
435 
436     // If we have mute controls, figure out the current setting.
437     if (has_mute()) {
438         res = FeatCtrlReq(proto, USB_AUDIO_GET_CUR, USB_AUDIO_MUTE_CONTROL, 0, &mute_cur_);
439         if (res != ZX_OK) {
440             return res;
441         }
442     }
443 
444     // If we have agc controls, figure out the current setting.
445     if (has_agc()) {
446         res = FeatCtrlReq(proto, USB_AUDIO_GET_CUR, USB_AUDIO_AUTOMATIC_GAIN_CONTROL, 0, &agc_cur_);
447         if (res != ZX_OK) {
448             return res;
449         }
450     }
451 
452     // Dump some diags info if TRACE level logging is enabled.
453     if (has_vol()) {
454         GLOBAL_LOG(TRACE,
455                    "FeatureUnit id %u: can%s mute, can%s AGC, gain [%.3f, %.3f: step %.3f] dB\n",
456                    id(),
457                    has_mute() ? "" : "not",
458                    has_agc() ? "" : "not",
459                    vol_min_db(), vol_max_db(), vol_res_db());
460     } else {
461         GLOBAL_LOG(TRACE, "FeatureUnit id %u: can%s mute, can%s AGC, and has fixed gain\n",
462                    id(), has_mute() ? "" : "not", has_agc() ? "" : "not");
463     }
464 
465     // All done!  Declare success and get out.
466     return ZX_OK;
467 };
468 
SetVol(const usb_protocol_t & proto,float db)469 float FeatureUnit::SetVol(const usb_protocol_t& proto, float db) {
470     // If we have no volume control, then our gain is fixed at 0.0 dB no matter
471     // what the user asks for.
472     if (!has_vol()) {
473         return 0.0;
474     }
475 
476     // Convert to our target value.  Start by converting to ticks.
477     float ticks_float = db / kDbPerTick;
478 
479     // Now snap to the closest allowed tick based on our resolution.
480     ticks_float = roundf(ticks_float / vol_res_) * vol_res_;
481 
482     // Now clamp to the acceptable min/max range and convert to integer ticks.
483     vol_cur_ = static_cast<int16_t>(fbl::clamp<float>(ticks_float, vol_min_, vol_max_));
484 
485     // Finally apply the setting.  If we have no explicit mute control, and we
486     // are currently supposed to be muted, skip this step.  We are using the
487     // volume control to simulate mute to the best of our abilities; we will
488     // restore vol_cur_ when the unit finally becomes un-muted.
489     if (!(mute_cur_ && !has_mute())) {
490         SetFeature(proto, USB_AUDIO_VOLUME_CONTROL, vol_cur_);
491     }
492 
493     return vol_cur_ * kDbPerTick;
494 }
495 
SetMute(const usb_protocol_t & proto,bool mute)496 bool FeatureUnit::SetMute(const usb_protocol_t& proto, bool mute) {
497     mute_cur_ = mute;
498 
499     // If we have an explicit mute control, use that.  Otherwise, do the best we
500     // can using the volume control (if present).
501     if (has_mute()) {
502         SetFeature(proto, USB_AUDIO_MUTE_CONTROL, mute_cur_);
503     } else {
504         // Section 5.2.2.4.3.2 of the USB Audio 1.0 spec defines int16::min as
505         // -inf dB for the purpose of setting gain.
506         int16_t tgt = mute ? std::numeric_limits<int16_t>::min() : vol_cur_;
507         SetFeature(proto, USB_AUDIO_VOLUME_CONTROL, tgt);
508     }
509 
510     return !!mute_cur_;
511 }
512 
SetAgc(const usb_protocol_t & proto,bool agc)513 bool FeatureUnit::SetAgc(const usb_protocol_t& proto, bool agc) {
514     if (has_agc()) {
515         agc_cur_ = agc;
516         SetFeature(proto, USB_AUDIO_AUTOMATIC_GAIN_CONTROL, static_cast<uint8_t>(agc));
517     }
518     return !!agc_cur_;
519 }
520 
Create(const DescriptorListMemory::Iterator & iter,uint8_t iid)521 fbl::RefPtr<ProcessingUnit> ProcessingUnit::Create(const DescriptorListMemory::Iterator& iter,
522                                                    uint8_t iid) {
523     // Find the size of each of the inlined variable length arrays in this
524     // structure, finding the locations of the constant headers in the process.
525     // If anything does not look right, complain and move on.
526     auto hdr0 = iter.hdr_as<usb_audio_ac_processing_unit_desc_0>();
527     if (hdr0 != nullptr) {
528         size_t off = sizeof(*hdr0) + hdr0->bNrInPins;
529         auto hdr1 = offset_ptr<usb_audio_ac_processing_unit_desc_1>(hdr0, off);
530         if (hdr1 != nullptr) {
531             off += sizeof(*hdr1) + hdr1->bControlSize;
532             auto hdr2 = offset_ptr<usb_audio_ac_processing_unit_desc_2>(hdr0, off);
533 
534             // TODO(johngro): additional sanity checking and pre-processing goes here.
535             //
536             // Note: Processing units actually come in their own pre-defined
537             // sub-flavors (determined by hdr0->wProcessType).  Instead of
538             // lumping them all together into one ProcessingUnit class, we
539             // should probably take the time to break them down into the various
540             // sub-flavors, at which point in time, the big validation switch
541             // statement would go somewhere in here.
542             //
543             // For now, however, we do not expect to have any need to control
544             // processing units.  If we ever encounter one, we really only want
545             // to understand the size of the baSourceID array so that we can
546             // successfully walk the graph when attempting to build input/output
547             // stream paths.
548             fbl::AllocChecker ac;
549             auto ret = fbl::AdoptRef(
550                     new (&ac) ProcessingUnit(iter.desc_list(), hdr0, hdr1, hdr2, iid));
551             return ac.check() ? ret : nullptr;
552         }
553     }
554 
555     GLOBAL_LOG(WARN, "ProcessingUnit header appears invalid @ offset %zu\n", iter.offset());
556     return nullptr;
557 }
558 
Create(const DescriptorListMemory::Iterator & iter,uint8_t iid)559 fbl::RefPtr<ExtensionUnit> ExtensionUnit::Create(const DescriptorListMemory::Iterator& iter,
560                                                  uint8_t iid) {
561     // Find the size of each of the inlined variable length arrays in this
562     // structure, finding the locations of the constant headers in the process.
563     // If anything does not look right, complain and move on.
564     auto hdr0 = iter.hdr_as<usb_audio_ac_extension_unit_desc_0>();
565     if (hdr0 != nullptr) {
566         size_t off = sizeof(*hdr0) + hdr0->bNrInPins;
567         auto hdr1 = offset_ptr<usb_audio_ac_extension_unit_desc_1>(hdr0, off);
568         if (hdr1 != nullptr) {
569             off += sizeof(*hdr1) + hdr1->bControlSize;
570             auto hdr2 = offset_ptr<usb_audio_ac_extension_unit_desc_2>(hdr0, off);
571 
572             // TODO(johngro): additional sanity checking and pre-processing goes here.
573             fbl::AllocChecker ac;
574             auto ret = fbl::AdoptRef(
575                     new (&ac) ExtensionUnit(iter.desc_list(), hdr0, hdr1, hdr2, iid));
576             return ac.check() ? ret : nullptr;
577         }
578     }
579 
580     GLOBAL_LOG(WARN, "ExtensionUnit header appears invalid @ offset %zu\n", iter.offset());
581     return nullptr;
582 }
583 
584 }  // namespace usb
585 }  // namespace audio
586