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