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 <audio-proto-utils/format-utils.h>
6 #include <fbl/algorithm.h>
7 #include <fbl/auto_call.h>
8
9 #include <utility>
10
11 #include "debug-logging.h"
12 #include "usb-audio-device.h"
13 #include "usb-audio-path.h"
14 #include "usb-audio-stream-interface.h"
15
16 namespace audio {
17 namespace usb {
18
19 // We use our parent's log prefix
log_prefix() const20 const char* UsbAudioStreamInterface::log_prefix() const {
21 return parent_.log_prefix();
22 }
23
Create(UsbAudioDevice * parent,DescriptorListMemory::Iterator * iter)24 fbl::unique_ptr<UsbAudioStreamInterface> UsbAudioStreamInterface::Create(
25 UsbAudioDevice* parent,
26 DescriptorListMemory::Iterator* iter) {
27 ZX_DEBUG_ASSERT(parent != nullptr);
28 ZX_DEBUG_ASSERT(iter != nullptr);
29
30 auto ihdr = iter->hdr_as<usb_interface_descriptor_t>();
31 ZX_DEBUG_ASSERT(ihdr); // The caller should have already verified this.
32 uint8_t iid = ihdr->bInterfaceNumber;
33
34 fbl::AllocChecker ac;
35 fbl::unique_ptr<UsbAudioStreamInterface> ret(
36 new (&ac) UsbAudioStreamInterface(parent, iter->desc_list(), iid));
37 if (ac.check()) {
38 zx_status_t res = ret->AddInterface(iter);
39 if (res == ZX_OK) {
40 return ret;
41 }
42 LOG_EX(ERROR, *parent,
43 "Failed to add initial interface (id %u) to UsbAudioStreamInterface (res %d)\n",
44 iid, res);
45 } else {
46 iter->Next(); // Success or failure, we are expected to consume this header.
47 LOG_EX(ERROR, *parent,
48 "Out of memory attempting to allocate UsbAudioStreamInterface (id %u)\n", iid);
49 }
50
51 return nullptr;
52 }
53
AddInterface(DescriptorListMemory::Iterator * iter)54 zx_status_t UsbAudioStreamInterface::AddInterface(DescriptorListMemory::Iterator* iter) {
55 // All of these checks should have been made by the caller already.
56 ZX_DEBUG_ASSERT(iter != nullptr);
57 ZX_DEBUG_ASSERT(iter->desc_list() == desc_list_);
58
59 auto ihdr = iter->hdr_as<usb_interface_descriptor_t>();
60 ZX_DEBUG_ASSERT(ihdr != nullptr);
61 ZX_DEBUG_ASSERT(ihdr->bInterfaceNumber == iid());
62
63 // No matter what, we need to consume the current descriptor header.
64 iter->Next();
65
66 // Make sure that this header represents a unique alternate setting.
67 auto alt_id = ihdr->bAlternateSetting;
68 auto fmt_iter = formats_.find_if([alt_id](const Format& fmt) -> bool {
69 return alt_id == fmt.alt_id();
70 });
71 if (fmt_iter.IsValid() || ((idle_hdr_ && (idle_hdr_->bAlternateSetting == alt_id)))) {
72 LOG(WARN,
73 "Skipping duplicate alternate setting ID in streaming interface descriptor. "
74 "(iid %u, alt_id %u)\n",
75 ihdr->bInterfaceNumber, alt_id);
76 // Don't return an error if we encounter a malformed header. Just skip
77 // it and do the best we can with what we have.
78 return ZX_OK;
79 }
80
81 // Examine the next descriptor. If it is an audio streaming class specific
82 // interface descriptor, then this top level descriptor is part of a
83 // described format. Otherwise, this is an empty alternate interface which
84 // is probably meant to be selected when this streaming interface is idle
85 // and should not be using any bus resources.
86 auto next_hdr = iter->hdr_as<usb_audio_desc_header>();
87 if ((next_hdr != nullptr) &&
88 (next_hdr->bDescriptorType == USB_AUDIO_CS_INTERFACE) &&
89 (next_hdr->bDescriptorSubtype == USB_AUDIO_AS_GENERAL)) {
90 auto aud_hdr = iter->hdr_as<usb_audio_as_header_desc>();
91 iter->Next();
92
93 if (aud_hdr == nullptr) {
94 LOG(WARN,
95 "Skipping badly formed alternate setting ID in streaming interface descriptor "
96 "(iid %u, alt_id %u).\n",
97 ihdr->bInterfaceNumber, alt_id);
98 return ZX_OK;
99 }
100
101 fbl::AllocChecker ac;
102 auto format = fbl::make_unique_checked<Format>(&ac, this, iter->desc_list(), ihdr, aud_hdr);
103 if (!ac.check()) {
104 LOG(ERROR, "Out of memory attempt to add Format to StreamInterface\n");
105 return ZX_ERR_NO_MEMORY;
106 }
107
108 zx_status_t status = format->Init(iter);
109 if (status != ZX_OK) {
110 LOG(WARN,
111 "Skipping bad format streaming interface descriptor. (iid %u, alt_id %u)\n",
112 ihdr->bInterfaceNumber, alt_id);
113 return ZX_OK;
114 }
115
116 // Make sure that the endpoint address and terminal link ID of this
117 // format matches all previously encountered formats.
118 //
119 // TODO(johngro) : It is unclear whether or not it makes any sense to
120 // have formats which link to different audio paths or have different
121 // endpoint addresses (implying potentially different directions). For
122 // now we simply skip these formats if we encounter them.
123 //
124 // If we ever encounter a device which has a mix of these parameters, we
125 // need come back and determine if there is a good generic approach for
126 // dealing with the situation.
127 if (!formats_.is_empty()) {
128 if (format->term_link() != term_link_) {
129 LOG(WARN,
130 "Skipping format (iid %u, alt_id %u) with non-uniform terminal ID "
131 "(expected %u, got %u)\n",
132 ihdr->bInterfaceNumber, alt_id, term_link_, format->term_link());
133 return ZX_OK;
134 }
135
136 if ((format->ep_addr() != ep_addr_) || (format->ep_attr() != ep_attr_)) {
137 LOG(ERROR,
138 "Skipping format (iid %u, alt_id %u) with non-uniform endpoint "
139 "address/attributes (expected 0x%02x/0x%02x, got 0x%02x/0x%02x)\n",
140 ihdr->bInterfaceNumber, alt_id,
141 ep_addr_, ep_attr_,
142 format->ep_addr(), format->ep_attr());
143 return ZX_OK;
144 }
145 } else {
146 term_link_ = format->term_link();
147 ep_addr_ = format->ep_addr();
148 ep_attr_ = format->ep_attr();
149 }
150
151 max_req_size_ = fbl::max(max_req_size_, format->max_req_size());
152 formats_.push_back(std::move(format));
153 } else {
154 if (idle_hdr_ == nullptr) {
155 idle_hdr_ = ihdr;
156 } else {
157 LOG(WARN,
158 "Skipping duplicate \"idle\" interface descriptor in streaming interface "
159 "descriptor. (iid %u, alt_id %u)\n",
160 ihdr->bInterfaceNumber, ihdr->bAlternateSetting);
161 }
162 }
163
164 return ZX_OK;
165 }
166
BuildFormatMap()167 zx_status_t UsbAudioStreamInterface::BuildFormatMap() {
168 if (format_map_.size()) {
169 LOG(WARN, "Attempted to re-build format map for streaming interface (iid %u)\n", iid());
170 return ZX_ERR_BAD_STATE;
171 }
172
173 // Make a pass over our list of formats and figure out how big our format
174 // map vector may need to be.
175 //
176 // Note: this is a rough worst case bound on how big the vector needs to be.
177 // Someday, we could come back here and compute a much tighter bound if we
178 // wanted to.
179 size_t worst_case_map_entries = 0;
180 for (const auto& fmt : formats_) {
181 // A frame rate count of 0 indicates a continuous format range which
182 // requires only one format range entry.
183 worst_case_map_entries += fmt.frame_rate_cnt() ? fmt.frame_rate_cnt() : 1;
184 }
185
186 // Now reserve our memory.
187 fbl::AllocChecker ac;
188 format_map_.reserve(worst_case_map_entries, &ac);
189 if (!ac.check()) {
190 LOG(ERROR, "Out of memory attempting to reserve %zu format ranges\n",
191 worst_case_map_entries);
192 return ZX_ERR_NO_MEMORY;
193 }
194
195 // Now iterate over our set and build the map.
196 for (const auto& fmt : formats_) {
197 // Record the min/max number of channels.
198 audio_stream_format_range_t range;
199 range.min_channels = fmt.ch_count();
200 range.max_channels = fmt.ch_count();
201
202 // Encode the sample container type from the type I format descriptor
203 // as an audio device driver audio_sample_format_t. If we encounter
204 // anything that we don't know how to encode, log a warning and skip the
205 // format.
206 //
207 auto tag = fmt.format_tag();
208 if (tag == USB_AUDIO_AS_FT_PCM8) {
209 if ((fmt.bit_resolution() != 8) || (fmt.subframe_bytes() != 1)) {
210 LOG(WARN, "Skipping PCM8 format with invalid bit res/subframe size (%u/%u)\n",
211 fmt.bit_resolution(), fmt.subframe_bytes());
212 continue;
213 }
214 range.sample_formats = static_cast<audio_sample_format_t>(
215 AUDIO_SAMPLE_FORMAT_8BIT | AUDIO_SAMPLE_FORMAT_FLAG_UNSIGNED);
216 } else
217 if (tag == USB_AUDIO_AS_FT_IEEE_FLOAT) {
218 if ((fmt.bit_resolution() != 32) || (fmt.subframe_bytes() != 4)) {
219 LOG(WARN, "Skipping IEEE_FLOAT format with invalid bit res/subframe size (%u/%u)\n",
220 fmt.bit_resolution(), fmt.subframe_bytes());
221 continue;
222 }
223 range.sample_formats = AUDIO_SAMPLE_FORMAT_32BIT_FLOAT;
224 } else
225 if (tag == USB_AUDIO_AS_FT_PCM) {
226 switch (fmt.bit_resolution()) {
227 case 8:
228 case 16:
229 case 32: {
230 if (fmt.subframe_bytes() != (fmt.bit_resolution() >> 3)) {
231 LOG(WARN,
232 "Skipping PCM format. Subframe size (%u bytes) does not "
233 "match Bit Res (%u bits)\n",
234 fmt.bit_resolution(),
235 fmt.subframe_bytes());
236 continue;
237 }
238 switch (fmt.bit_resolution()) {
239 case 8: range.sample_formats = AUDIO_SAMPLE_FORMAT_8BIT; break;
240 case 16: range.sample_formats = AUDIO_SAMPLE_FORMAT_16BIT; break;
241 case 32: range.sample_formats = AUDIO_SAMPLE_FORMAT_32BIT; break;
242 }
243 } break;
244
245 case 20:
246 case 24: {
247 if ((fmt.subframe_bytes() != 3) && (fmt.subframe_bytes() != 4)) {
248 LOG(WARN,
249 "Skipping PCM format. %u-bit audio must be packed into a 3 "
250 "or 4 byte subframe (Subframe size %u)\n",
251 fmt.bit_resolution(),
252 fmt.subframe_bytes());
253 continue;
254 }
255 switch (fmt.bit_resolution()) {
256 case 20:
257 range.sample_formats = ((fmt.subframe_bytes() == 3)
258 ? AUDIO_SAMPLE_FORMAT_20BIT_PACKED
259 : AUDIO_SAMPLE_FORMAT_20BIT_IN32);
260 break;
261 case 24:
262 range.sample_formats = ((fmt.subframe_bytes() == 3)
263 ? AUDIO_SAMPLE_FORMAT_24BIT_PACKED
264 : AUDIO_SAMPLE_FORMAT_24BIT_IN32);
265 break;
266 }
267 } break;
268
269 default:
270 LOG(WARN, "Skipping PCM format with unsupported bit res (%u bits)\n",
271 fmt.bit_resolution());
272 continue;
273 }
274 } else {
275 LOG(WARN, "Skipping unsupported format tag (%u)\n", tag);
276 continue;
277 }
278
279 // Now pack the supported frame rates. A format with a frame rate count of
280 // 0 is a continuous range of frame rates. Otherwise, we pack each discrete
281 // frame rate as an individual entry.
282 //
283 // TODO(johngro) : Discrete frame rates could be encoded more compactly
284 // if wanted to do so by extracting all of the 48k and 44.1k rates into
285 // a bitmask, and then putting together ranges which represented
286 // continuous runs of frame rates in each of the families.
287 if (fmt.frame_rate_cnt()) {
288 for (uint8_t i = 0; i < fmt.frame_rate_cnt(); ++i) {
289 uint32_t rate = fmt.frame_rate(i);
290 range.min_frames_per_second = rate;
291 range.max_frames_per_second = rate;
292
293 if (audio::utils::FrameRateIn48kFamily(rate)) {
294 range.flags = ASF_RANGE_FLAG_FPS_48000_FAMILY;
295 } else
296 if (audio::utils::FrameRateIn441kFamily(rate)) {
297 range.flags = ASF_RANGE_FLAG_FPS_44100_FAMILY;
298 } else {
299 range.flags = ASF_RANGE_FLAG_FPS_CONTINUOUS;
300 }
301
302 format_map_.push_back({ range, fmt.alt_id(), fmt.ep_addr(), fmt.max_req_size() });
303 }
304 } else {
305 range.min_frames_per_second = fmt.min_cont_frame_rate();
306 range.max_frames_per_second = fmt.max_cont_frame_rate();
307 range.flags = ASF_RANGE_FLAG_FPS_CONTINUOUS;
308 format_map_.push_back({ range, fmt.alt_id(), fmt.ep_addr(), fmt.max_req_size() });
309 }
310 }
311
312 // If we failed to encode *any* valid format ranges, log a warning and
313 // return an error. This stream interface is not going to be useful to us.
314 if (format_map_.is_empty()) {
315 LOG(WARN, "Failed to find any usable formats for streaming interface (iid %u)\n", iid());
316 return ZX_ERR_NOT_SUPPORTED;
317 }
318
319 return ZX_OK;
320 }
321
LookupFormat(uint32_t frames_per_second,uint16_t channels,audio_sample_format_t sample_format,size_t * out_format_ndx)322 zx_status_t UsbAudioStreamInterface::LookupFormat(uint32_t frames_per_second,
323 uint16_t channels,
324 audio_sample_format_t sample_format,
325 size_t* out_format_ndx) {
326 if (out_format_ndx == nullptr) {
327 return ZX_ERR_INVALID_ARGS;
328 }
329
330 *out_format_ndx = format_map_.size();
331
332 // Search our format map to find the alternate interface setting which
333 // supports the requested format.
334 for (size_t i = 0; i < format_map_.size(); ++i) {
335 if (audio::utils::FormatIsCompatible(frames_per_second,
336 channels,
337 sample_format,
338 format_map_[i].range_)) {
339 *out_format_ndx = i;
340 return ZX_OK;
341 }
342 }
343
344 return ZX_ERR_NOT_SUPPORTED;
345 }
346
ActivateFormat(size_t ndx,uint32_t frames_per_second)347 zx_status_t UsbAudioStreamInterface::ActivateFormat(size_t ndx, uint32_t frames_per_second) {
348 if (ndx >= format_map_.size()) {
349 return ZX_ERR_INVALID_ARGS;
350 }
351
352 // Select the interface used for this format, then configure the endpoint
353 // for the requested frame rate. user know what the maximum request size is
354 // for this interface.
355 zx_status_t status;
356 const auto& f = format_map_[ndx];
357 status = usb_set_interface(&parent_.usb_proto(), iid(), f.alt_id_);
358 if (status != ZX_OK) {
359 LOG(ERROR,
360 "Failed to select interface (id %u, alt %u, ep %u) "
361 "when configuring format ndx %zu (status %d)\n",
362 iid(), f.alt_id_, f.ep_addr_, ndx, status);
363 return status;
364 }
365
366 // Do not attempt to set the sample rate if the endpoint supports
367 // only one. In theory, devices should ignore this request, but in
368 // practice, some devices will refuse the command entirely, and we
369 // will get ZX_ERR_IO_REFUSED back from the bus driver.
370 //
371 // Note: This method of determining whether or not an endpoint
372 // supports only a single rate only works here because we currently
373 // demand that all of our formats share a single endpoint address.
374 // If this changes in the future, this heuristic will need to be
375 // revisited.
376 bool single_rate = (format_map_.size() == 1) &&
377 !(format_map_[0].range_.flags & ASF_RANGE_FLAG_FPS_CONTINUOUS);
378 if (!single_rate) {
379 // See section 5.2.3.2.3.1 of the USB Audio 1.0 spec.
380 uint8_t buffer[3];
381 buffer[0] = static_cast<uint8_t>(frames_per_second);
382 buffer[1] = static_cast<uint8_t>(frames_per_second >> 8);
383 buffer[2] = static_cast<uint8_t>(frames_per_second >> 16);
384 status = usb_control_out(&parent_.usb_proto(),
385 USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
386 USB_AUDIO_SET_CUR,
387 USB_AUDIO_SAMPLING_FREQ_CONTROL << 8,
388 f.ep_addr_, ZX_TIME_INFINITE,
389 &buffer, sizeof(buffer));
390 if (status != ZX_OK) {
391 if (status == ZX_ERR_IO_REFUSED || status == ZX_ERR_IO_INVALID) {
392 // clear the stall/error
393 usb_reset_endpoint(&parent_.usb_proto(), f.ep_addr_);
394 }
395
396 LOG(ERROR, "Failed to set frame rate %u for ep address %u (status %d)\n",
397 frames_per_second, f.ep_addr_, status);
398
399 return status;
400 }
401 }
402
403 return ZX_OK;
404 }
405
ActivateIdleFormat()406 zx_status_t UsbAudioStreamInterface::ActivateIdleFormat() {
407 if (idle_hdr_ == nullptr) {
408 return ZX_ERR_NOT_SUPPORTED;
409 }
410
411 ZX_DEBUG_ASSERT(idle_hdr_->bInterfaceNumber == iid());
412 return usb_set_interface(&parent_.usb_proto(), iid(), idle_hdr_->bAlternateSetting);
413 }
414
LinkPath(fbl::unique_ptr<AudioPath> path)415 void UsbAudioStreamInterface::LinkPath(fbl::unique_ptr<AudioPath> path) {
416 ZX_DEBUG_ASSERT(path != nullptr);
417 ZX_DEBUG_ASSERT(path_ == nullptr);
418 ZX_DEBUG_ASSERT(direction() == path->direction());
419 ZX_DEBUG_ASSERT(term_link() == path->stream_terminal().id());
420 path_ = std::move(path);
421 }
422
Init(DescriptorListMemory::Iterator * iter)423 zx_status_t UsbAudioStreamInterface::Format::Init(DescriptorListMemory::Iterator* iter) {
424 ZX_DEBUG_ASSERT(iter != nullptr);
425 ZX_DEBUG_ASSERT(iter->desc_list() == desc_list_);
426
427 // Skip formats tags that we currently do not support or know how to deal
428 // with. Right now, we only deal with the linear PCM forms of Type I audio
429 // formats.
430 switch (class_hdr_->wFormatTag) {
431 case USB_AUDIO_AS_FT_PCM:
432 case USB_AUDIO_AS_FT_PCM8:
433 case USB_AUDIO_AS_FT_IEEE_FLOAT:
434 break;
435
436 default:
437 LOG(ERROR,
438 "Unsupported format tag (0x%04hx) in class specific audio stream interface "
439 "(iid %u, alt_id %u)\n",
440 class_hdr_->wFormatTag, interface_hdr_->bInterfaceNumber, alt_id());
441 return ZX_ERR_NOT_SUPPORTED;
442 }
443
444 // Next go looking for the other headers we will need in order to operate.
445 // In specific, we need to find an audio format descriptor (specifically a
446 // Type I descriptor), a general USB Endpoint descriptor, and a audio class
447 // specific endpoint descriptor.
448 //
449 // If we encounter something which is not one of these things, then we have
450 // run out of headers to parse.
451 //
452 // If we encounter duplicates of these descriptors, or we encounter
453 // something clearly incompatible (such as a type II or type III format
454 // descriptor), then we are confused and this interface should be ignored.
455 // Be sure to skip headers like this if we return from the middle of the
456 // do/while loop below.
457 auto cleanup = fbl::MakeAutoCall([iter]() { iter->Next(); });
458 do {
459 auto hdr = iter->hdr();
460 if (hdr == nullptr) {
461 break;
462 }
463
464 if (hdr->bDescriptorType == USB_AUDIO_CS_INTERFACE) {
465 // Stop parsing if this is not an audio format type descriptor
466 auto ihdr = iter->hdr_as<usb_audio_desc_header>();
467 if ((ihdr == nullptr) || (ihdr->bDescriptorSubtype != USB_AUDIO_AS_FORMAT_TYPE)) {
468 break;
469 }
470
471 auto fmt_hdr = iter->hdr_as<usb_audio_as_format_type_hdr>();
472 if (fmt_hdr == nullptr) {
473 break;
474 }
475
476 if (fmt_hdr->bFormatType != USB_AUDIO_FORMAT_TYPE_I) {
477 LOG(ERROR,
478 "Unsupported format type (%u) in class specific audio stream format type "
479 "interface (iid %u, alt_id %u)\n",
480 fmt_hdr->bFormatType, interface_hdr_->bInterfaceNumber, alt_id());
481 return ZX_ERR_NOT_SUPPORTED;
482 }
483
484 auto fmt_desc = iter->hdr_as<usb_audio_as_format_type_i_desc>();
485 if ((fmt_desc_ != nullptr) || (fmt_desc == nullptr)) {
486 LOG(ERROR,
487 "Malformed or duplicate type 1 format type descriptor in class specific audio "
488 "interface (iid %u, alt_id %u)\n",
489 interface_hdr_->bInterfaceNumber, alt_id());
490 return ZX_ERR_NOT_SUPPORTED;
491 }
492
493 // Stash the pointer, we'll sanity check a bit more once we are finished finding
494 // headers.
495 fmt_desc_ = fmt_desc;
496 } else
497 if (hdr->bDescriptorType == USB_DT_ENDPOINT) {
498 auto ep_desc = iter->hdr_as<usb_endpoint_descriptor_t>();
499 if (ep_desc == nullptr) {
500 LOG(ERROR,
501 "Malformed standard endpoint descriptor in class specific audio interface "
502 "(iid %u, alt_id %u)\n",
503 interface_hdr_->bInterfaceNumber, alt_id());
504 return ZX_ERR_NOT_SUPPORTED;
505 }
506
507 // TODO(johngro): Come back and fix this. There are devices with
508 // multiple isochronous endpoints per format interface. Device
509 // which use an isochronous output endpoint with an Asynchronous
510 // sync type seem to have an isochronous input endpoint as well
511 // which is probably used for clock recovery. Instead of
512 // skipping/ignoring this endpoint, we really should be using it to
513 // recover the device clock.
514 if (ep_desc_ != nullptr) {
515 LOG(WARN,
516 "Skipping duplicate standard endpoint descriptor in class specific audio "
517 "interface (iid %u, alt_id %u, ep_addr %u)\n",
518 interface_hdr_->bInterfaceNumber, alt_id(), ep_desc->bEndpointAddress);
519 } else {
520 if ((usb_ep_type(ep_desc) != USB_ENDPOINT_ISOCHRONOUS) ||
521 (usb_ep_sync_type(ep_desc) == USB_ENDPOINT_NO_SYNCHRONIZATION)) {
522 LOG(WARN,
523 "Skipping endpoint descriptor with unsupported attributes "
524 "interface (iid %u, alt_id %u, ep_attr 0x%02x)\n",
525 interface_hdr_->bInterfaceNumber, alt_id(), ep_desc->bmAttributes);
526 } else {
527 ep_desc_ = ep_desc;
528 }
529 }
530 } else
531 if (hdr->bDescriptorType == USB_AUDIO_CS_ENDPOINT) {
532 // Stop parsing if this is not a class specific AS isochronous endpoint descriptor
533 auto ihdr = iter->hdr_as<usb_audio_desc_header>();
534 if ((ihdr == nullptr) || (ihdr->bDescriptorSubtype != USB_AUDIO_EP_GENERAL)) {
535 break;
536 }
537
538 auto class_ep_desc = iter->hdr_as<usb_audio_as_isoch_ep_desc>();
539 if (class_ep_desc == nullptr) {
540 LOG(ERROR,
541 "Malformed or class specific endpoint descriptor in class specific audio "
542 "interface (iid %u, alt_id %u)\n",
543 interface_hdr_->bInterfaceNumber, alt_id());
544 return ZX_ERR_NOT_SUPPORTED;
545 }
546
547 if (class_ep_desc_ != nullptr) {
548 LOG(WARN,
549 "Skipping duplicate class specific endpoint descriptor in class specific "
550 "audio interface (iid %u, alt_id %u\n",
551 interface_hdr_->bInterfaceNumber, alt_id());
552 } else {
553 class_ep_desc_ = class_ep_desc;
554 }
555 } else {
556 // We don't recognize this descriptor, so we have run out of
557 // descriptors that we beleive belong to this format. Move on to
558 // sanity checks.
559 break;
560 }
561 } while (iter->Next());
562 cleanup.cancel();
563
564 // Sanity check what we have found so far. Right now, we need to have found...
565 //
566 // 1) A Type I audio format type descriptor (PCM)
567 // 2) A standard Isochronous USB endpoint descriptor.
568 // 3) An audio class specific endpoint descriptor.
569 //
570 // In addition, we need to make sure that the range of frame rates present
571 // in the Type I descriptor makes sense. If the range is continuous, the
572 // array must contain *exactly* 2 entries. If the range is discrete, then
573 // the array must contain an integer number of entries, and must contain at
574 // least one entry.
575 if ((fmt_desc_ == nullptr) || (ep_desc_ == nullptr) || (class_ep_desc_ == nullptr)) {
576 LOG(ERROR,
577 "Missing one or more required descriptors in audio interface (iid %u, alt_id %u); "
578 "Missing%s%s%s\n",
579 interface_hdr_->bInterfaceNumber, alt_id(),
580 (fmt_desc_ == nullptr) ? " [Type I Format Type Descriptor]" : "",
581 (ep_desc_ == nullptr) ? " [Standard Endpoint Descriptor]" : "",
582 (class_ep_desc_ == nullptr) ? " [Class Endpoint Descriptor]" : "");
583 return ZX_ERR_NOT_SUPPORTED;
584 }
585
586 // hdr_as<> should have already verified this for us.
587 ZX_DEBUG_ASSERT(fmt_desc_->bLength >= sizeof(*fmt_desc_));
588
589 // Sanity check the size of the frame rate table.
590 size_t expected_bytes = (frame_rate_cnt() ? frame_rate_cnt() : 2)
591 * sizeof(usb_audio_as_samp_freq);
592 size_t extra_bytes = fmt_desc_->bLength - sizeof(*fmt_desc_);
593 if (expected_bytes != extra_bytes) {
594 LOG(ERROR,
595 "Bad frame rate table size in type 1 audio format type descriptor in audio interface "
596 "(iid %u, alt_id %u). Expected %zu, Got %zu\n",
597 interface_hdr_->bInterfaceNumber, alt_id(), expected_bytes, extra_bytes);
598 return ZX_ERR_INTERNAL;
599 }
600
601 // If this is a continuous range of frame rates, then the min/max order needs to be correct.
602 if ((frame_rate_cnt() == 0) && (min_cont_frame_rate() > max_cont_frame_rate())) {
603 LOG(ERROR,
604 "Invalid continuous frame rate range [%u, %u] type 1 audio format type descriptor in "
605 "audio interface (iid %u, alt_id %u).\n",
606 min_cont_frame_rate(), max_cont_frame_rate(),
607 interface_hdr_->bInterfaceNumber, alt_id());
608 return ZX_ERR_INTERNAL;
609 }
610
611 return ZX_OK;
612 }
613
614 } // namespace usb
615 } // namespace audio
616