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 <dispatcher-pool/dispatcher-thread-pool.h> 6#include <fbl/auto_call.h> 7#include <fbl/auto_lock.h> 8#include <fbl/intrusive_double_list.h> 9#include <string.h> 10 11#include "usb-audio.h" 12#include "usb-audio-device.h" 13#include "usb-audio-stream.h" 14#include "usb-audio-stream-interface.h" 15 16namespace audio { 17namespace usb { 18 19zx_status_t UsbAudioDevice::DriverBind(zx_device_t* parent) { 20 fbl::AllocChecker ac; 21 auto usb_device = fbl::AdoptRef(new (&ac) audio::usb::UsbAudioDevice(parent)); 22 if (!ac.check()) { 23 return ZX_ERR_NO_MEMORY; 24 } 25 26 zx_status_t status = usb_device->Bind(); 27 if (status != ZX_OK) { 28 return status; 29 } 30 31 // We have transfered our fbl::RefPtr reference to the C ddk. We will 32 // recover it (someday) when the release hook is called. Until then, we 33 // need to deliberately leak our reference so that we do not destruct as we 34 // exit this function. 35 __UNUSED UsbAudioDevice* leaked_ref; 36 leaked_ref = usb_device.leak_ref(); 37 return status; 38} 39 40UsbAudioDevice::UsbAudioDevice(zx_device_t* parent) : UsbAudioDeviceBase(parent) { 41 ::memset(&usb_proto_, 0, sizeof(usb_proto_)); 42 ::memset(&usb_dev_desc_, 0, sizeof(usb_dev_desc_)); 43 snprintf(log_prefix_, sizeof(log_prefix_), "UsbAud Unknown"); 44} 45 46void UsbAudioDevice::RemoveAudioStream(const fbl::RefPtr<UsbAudioStream>& stream) { 47 fbl::AutoLock lock(&lock_); 48 ZX_DEBUG_ASSERT(stream != nullptr); 49 if (stream->InContainer()) { 50 streams_.erase(*stream); 51 } 52} 53 54zx_status_t UsbAudioDevice::Bind() { 55 zx_status_t status; 56 57 // Fetch our protocol. We will need it to do pretty much anything with this 58 // device. 59 status = device_get_protocol(parent(), ZX_PROTOCOL_USB, &usb_proto_); 60 if (status != ZX_OK) { 61 LOG(ERROR, "Failed to get USB protocol thunks (status %d)\n", status); 62 return status; 63 } 64 65 usb_composite_protocol_t usb_composite_proto; 66 status = device_get_protocol(parent(), ZX_PROTOCOL_USB_COMPOSITE, &usb_composite_proto); 67 if (status != ZX_OK) { 68 LOG(ERROR, "Failed to get USB composite protocol thunks (status %d)\n", status); 69 return status; 70 } 71 72 // Fetch our top level device descriptor, so we know stuff like the values 73 // of our VID/PID. 74 usb_get_device_descriptor(&usb_proto_, &usb_dev_desc_); 75 snprintf(log_prefix_, sizeof(log_prefix_), "UsbAud %04x:%04x", vid(), pid()); 76 77 // Attempt to cache the string descriptors for our manufacturer name, 78 // product name, and serial number. 79 if (usb_dev_desc_.iManufacturer) { 80 mfr_name_ = FetchStringDescriptor(usb_proto_, usb_dev_desc_.iManufacturer); 81 } 82 83 if (usb_dev_desc_.iProduct) { 84 prod_name_ = FetchStringDescriptor(usb_proto_, usb_dev_desc_.iProduct); 85 } 86 87 if (usb_dev_desc_.iSerialNumber) { 88 serial_num_ = FetchStringDescriptor(usb_proto_, usb_dev_desc_.iSerialNumber); 89 } 90 91 // Our top level binding script has only claimed audio interfaces with a 92 // subclass of control. Go ahead and claim anything which has a top level 93 // class of of "audio"; this is where we will find our Audio and MIDI 94 // streaming interfaces. 95 status = usb_claim_additional_interfaces( 96 &usb_composite_proto, 97 [](usb_interface_descriptor_t* intf, void* arg) -> bool { 98 return (intf->bInterfaceClass == USB_CLASS_AUDIO && 99 intf->bInterfaceSubClass != USB_SUBCLASS_AUDIO_CONTROL); 100 }, 101 NULL); 102 if (status != ZX_OK) { 103 LOG(ERROR, "Failed to claim additional audio interfaces (status %d)\n", status); 104 return status; 105 } 106 107 // Allocate and read in our descriptor list. 108 desc_list_ = DescriptorListMemory::Create(&usb_proto_); 109 if (desc_list_ == nullptr) { 110 LOG(ERROR, "Failed to fetch descriptor list\n"); 111 return status; 112 } 113 114 // Publish our device. 115 status = DdkAdd("usb-audio-ctrl"); 116 if (status != ZX_OK) { 117 return status; 118 } 119 120 Probe(); 121 122 return ZX_OK; 123} 124 125void UsbAudioDevice::Probe() { 126 // A reference to the audio control interface along with the set of audio 127 // stream interfaces that we discover during probing. We will need at least 128 // one control interface and one or more usable streaming audio interface if 129 // we want to publish *any* audio streams. 130 fbl::unique_ptr<UsbAudioControlInterface> control_ifc; 131 fbl::DoublyLinkedList<fbl::unique_ptr<UsbAudioStreamInterface>> aud_stream_ifcs; 132 133 // Go over our descriptor list. Right now, we are looking for only three 134 // things; The Audio Control interface, and the various Audio/MIDI Streaming 135 // interfaces. 136 DescriptorListMemory::Iterator iter(desc_list_); 137 while (iter.valid()) { 138 // Advance to the next descriptor if we don't find and parse an 139 // interface we understand. 140 auto cleanup = fbl::MakeAutoCall([&iter] { iter.Next(); }); 141 auto hdr = iter.hdr(); 142 143 // We are only prepared to find interface descriptors at this point. 144 if (hdr->bDescriptorType != USB_DT_INTERFACE) { 145 LOG(WARN, "Skipping unexpected descriptor (len = %u, type = %u)\n", 146 hdr->bLength, hdr->bDescriptorType); 147 continue; 148 } 149 150 auto ihdr = iter.hdr_as<usb_interface_descriptor_t>(); 151 if (ihdr == nullptr) { 152 LOG(WARN, "Skipping bad interface descriptor header @ offset %zu/%zu\n", 153 iter.offset(), iter.desc_list()->size()); 154 continue; 155 } 156 157 if ((ihdr->bInterfaceClass != USB_CLASS_AUDIO) || 158 ((ihdr->bInterfaceSubClass != USB_SUBCLASS_AUDIO_CONTROL) && 159 (ihdr->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING) && 160 (ihdr->bInterfaceSubClass != USB_SUBCLASS_MIDI_STREAMING))) { 161 LOG(WARN, "Skipping unknown interface (class %u, subclass %u)\n", 162 ihdr->bInterfaceClass, ihdr->bInterfaceSubClass); 163 continue; 164 } 165 166 switch (ihdr->bInterfaceSubClass) { 167 case USB_SUBCLASS_AUDIO_CONTROL: { 168 if (control_ifc != nullptr) { 169 LOG(WARN, "More than one audio control interface detected, skipping.\n"); 170 break; 171 } 172 173 auto control = UsbAudioControlInterface::Create(this); 174 if (control == nullptr) { 175 LOG(WARN, "Failed to allocate audio control interface\n"); 176 break; 177 } 178 179 // Give the control interface a chance to parse it's contents. 180 // Success or failure, when we are finished, the iterator should 181 // have been advanced to the next descriptor which does not make 182 // sense to the control interface parser. Cancel the cleanup 183 // task so that it does not skip over this header. 184 zx_status_t res = control->Initialize(&iter); 185 cleanup.cancel(); 186 if (res == ZX_OK) { 187 // No need to log in case of failure, the interface class 188 // should already have done so. 189 control_ifc = fbl::move(control); 190 } 191 break; 192 } 193 194 case USB_SUBCLASS_AUDIO_STREAMING: { 195 // We recognize this header and are going to consume it (whether or 196 // not we successfully create or add to an existing audio stream 197 // interface). Cancel the cleanup lambda so that it does not skip 198 // the next header as well. 199 cleanup.cancel(); 200 201 // Check to see if this is a new interface, or an alternate 202 // interface description for an existing stream interface. 203 uint8_t iid = ihdr->bInterfaceNumber; 204 auto ifc_iter = aud_stream_ifcs.find_if( 205 [iid](const UsbAudioStreamInterface& ifc) -> bool { return ifc.iid() == iid; }); 206 207 if (ifc_iter.IsValid()) { 208 zx_status_t res = ifc_iter->AddInterface(&iter); 209 if (res != ZX_OK) { 210 LOG(WARN, "Failed to add audio stream interface (id %u) @ offset %zu/%zu\n", 211 iid, iter.offset(), iter.desc_list()->size()); 212 } 213 } else { 214 auto ifc = UsbAudioStreamInterface::Create(this, &iter); 215 if (ifc == nullptr) { 216 LOG(WARN, 217 "Failed to create audio stream interface (id %u) @ offset %zu/%zu\n", 218 iid, iter.offset(), iter.desc_list()->size()); 219 } else { 220 LOG(TRACE, "Discovered new audio streaming interface (id %u)\n", iid); 221 aud_stream_ifcs.push_back(fbl::move(ifc)); 222 } 223 } 224 break; 225 } 226 227 // TODO(johngro): Do better than this for MIDI streaming interfaces. 228 // We should probably mirror the pattern used for the audio 229 // streaming interfaces where we create a class to hold all of the 230 // interfaces along with their descriptors and alternate interface 231 // variants, then pass that class on to a driver class assuming that 232 // everything checks out. 233 // 234 // Right now, we just look for a top level interface descriptor 235 // along with a single endpoint descriptor, and skip pretty much 236 // everything else. 237 case USB_SUBCLASS_MIDI_STREAMING: { 238 // We recognize this header and are going to consume it (whether or 239 // not we successfully create or add to an existing audio stream 240 // interface). Cancel the cleanup lambda so that it does not skip 241 // the next header as well. 242 cleanup.cancel(); 243 244 // Go looking for the endpoint descriptor which goes with this 245 // streaming descriptor. If we find one, attempt to publish a device. 246 struct MidiStreamingInfo info(ihdr); 247 ParseMidiStreamingIfc(&iter, &info); 248 249 if (info.out_ep != nullptr) { 250 LOG(TRACE, "Adding MIDI sink (iid %u, ep 0x%02x)\n", 251 info.ifc->bInterfaceNumber, info.out_ep->bEndpointAddress); 252 usb_midi_sink_create(zxdev(), 253 &usb_proto_, 254 midi_sink_index_++, 255 info.ifc, 256 info.out_ep); 257 } 258 259 if (info.in_ep != nullptr) { 260 LOG(TRACE, "Adding MIDI source (iid %u, ep 0x%02x)\n", 261 info.ifc->bInterfaceNumber, info.in_ep->bEndpointAddress); 262 usb_midi_source_create(zxdev(), 263 &usb_proto_, 264 midi_source_index_++, 265 info.ifc, 266 info.in_ep); 267 } 268 269 break; 270 } // case 271 } // switch 272 } 273 274 if ((control_ifc == nullptr) && !aud_stream_ifcs.is_empty()) { 275 LOG(WARN, "No control interface discovered. Discarding all audio streaming interfaces\n"); 276 aud_stream_ifcs.clear(); 277 } 278 279 // Now that we are done parsing all of our descriptors, go over our list of 280 // audio streaming interfaces and pair each up with the appropriate audio 281 // path as we go. Create an actual Fuchsia audio stream for each valid 282 // streaming interface with a a valid audio path. 283 while (!aud_stream_ifcs.is_empty()) { 284 // Build the format map for this stream interface. If we cannot find 285 // any usable formats for this streaming interface, simply discard it. 286 auto stream_ifc = aud_stream_ifcs.pop_front(); 287 zx_status_t status = stream_ifc->BuildFormatMap(); 288 if (status != ZX_OK) { 289 LOG(ERROR, "Failed to build format map for streaming interface id %u (status %d)\n", 290 stream_ifc->iid(), status); 291 continue; 292 } 293 294 // Find the path which goes with this interface. 295 auto path = control_ifc->ExtractPath(stream_ifc->term_link(), stream_ifc->direction()); 296 if (path == nullptr) { 297 LOG(WARN, 298 "Discarding audio streaming interface (id %u) as we could not find a path to match " 299 "its terminal link ID (%u) and direction (%u)\n", 300 stream_ifc->iid(), 301 stream_ifc->term_link(), 302 static_cast<uint32_t>(stream_ifc->direction())); 303 continue; 304 } 305 306 // Link the path to the stream interface. 307 LOG(TRACE, "Linking streaming interface id %u to audio path terminal %u\n", 308 stream_ifc->iid(), path->stream_terminal().id()); 309 stream_ifc->LinkPath(fbl::move(path)); 310 311 // Log a warning if we are about to build an audio path which operates 312 // in separate clock domain. We still need to add support for this 313 // case, see ZX-2044 for details. 314 if (stream_ifc->ep_sync_type() == EndpointSyncType::Async) { 315 LOG(WARN, 316 "Warning: Creating USB audio %s with operating in Asynchronous Isochronous mode. " 317 "See ZX-2044\n", 318 stream_ifc->direction() == Direction::Input ? "input" : "output"); 319 } 320 321 // Create a new audio stream, handing the stream interface over to it. 322 auto stream = UsbAudioStream::Create(this, fbl::move(stream_ifc)); 323 if (stream == nullptr) { 324 // No need to log an error, the Create method has already done so. 325 continue; 326 } 327 328 // Make sure that the stream is being tracked in our streams_ collection 329 // before attemping to publish its device node. 330 { 331 fbl::AutoLock lock(&lock_); 332 streams_.push_back(stream); 333 } 334 335 // Publish the new stream. If something goes wrong, take it out of the 336 // streams_ collection. 337 status = stream->Bind(); 338 if (status != ZX_OK) { 339 // Again, no need to log. Bind will have already logged any error. 340 RemoveAudioStream(stream); 341 } 342 } 343} 344 345void UsbAudioDevice::ParseMidiStreamingIfc(DescriptorListMemory::Iterator* iter, 346 MidiStreamingInfo* inout_info) { 347 MidiStreamingInfo& info = *inout_info; 348 349 ZX_DEBUG_ASSERT(iter != nullptr); 350 ZX_DEBUG_ASSERT(inout_info != nullptr); 351 ZX_DEBUG_ASSERT(inout_info->ifc != nullptr); 352 353 // Go looking for the endpoint descriptor which goes with this 354 // streaming descriptor. Try to consume all of the descriptors 355 // which go with this MIDI streaming descriptor as we go. 356 while (iter->Next()) { 357 auto hdr = iter->hdr(); 358 359 switch (hdr->bDescriptorType) { 360 // Generic interface 361 case USB_DT_INTERFACE: { 362 auto ihdr = iter->hdr_as<usb_interface_descriptor_t>(); 363 if (ihdr == nullptr) { 364 return; 365 } 366 367 // If this is not a midi streaming interface, or it is a midi 368 // streaming interface with a different interface id, than the ones 369 // we have been seeing, then we are done. 370 if ((ihdr->bInterfaceSubClass != USB_SUBCLASS_MIDI_STREAMING) || 371 (ihdr->bInterfaceNumber != info.ifc->bInterfaceNumber)) { 372 return; 373 } 374 375 // If we have already found an endpoint which goes with an 376 // interface, then this is another alternate setting (either 377 // with an endpoint or an idle alternate settings). In a 378 // more complicated world, we should handle this, but for 379 // now we just log a warning and skip it. 380 if ((info.out_ep != nullptr) || (info.in_ep != nullptr)) { 381 LOG(WARN, 382 "Multiple alternate settings found for MIDI streaming interface " 383 "(iid %u, alt %u)\n", 384 ihdr->bInterfaceNumber, 385 ihdr->bAlternateSetting); 386 continue; 387 } 388 389 // Stash this as the most recent MIDI streaming interface we have 390 // discovered, and keep parsing looking for the associated 391 // endpoint(s). 392 info.ifc = ihdr; 393 } break; 394 395 // Class specific interface 396 case USB_AUDIO_CS_INTERFACE: { 397 auto aud_hdr = iter->hdr_as<usb_audio_desc_header>(); 398 if (aud_hdr == nullptr) { 399 return; 400 } 401 402 // Silently skip the class specific MIDI headers which go 403 // along with this streaming interface descriptor. 404 if ((aud_hdr->bDescriptorSubtype == USB_MIDI_MS_HEADER) || 405 (aud_hdr->bDescriptorSubtype == USB_MIDI_IN_JACK) || 406 (aud_hdr->bDescriptorSubtype == USB_MIDI_OUT_JACK) || 407 (aud_hdr->bDescriptorSubtype == USB_MIDI_ELEMENT)) { 408 LOG(SPEW, "Skipping class specific MIDI interface subtype = %u\n", 409 aud_hdr->bDescriptorSubtype); 410 continue; 411 } 412 413 // We don't recognize this class specific interface header. Stop 414 // parsing. 415 return; 416 } break; 417 418 // Generic Endpoint 419 case USB_DT_ENDPOINT: { 420 auto ep_desc = iter->hdr_as<usb_endpoint_descriptor_t>(); 421 if (ep_desc == nullptr) { 422 return; 423 } 424 425 // If this is not a bulk transfer endpoint, then we are not quite sure what to do with 426 // it. Log a warning and skip it. 427 if (usb_ep_type(ep_desc) != USB_ENDPOINT_BULK) { 428 LOG(WARN, 429 "Skipping Non-bulk transfer endpoint (%u) found for MIDI streaming interface " 430 "(iid %u, alt %u)\n", 431 usb_ep_type(ep_desc), 432 info.ifc->bInterfaceNumber, 433 info.ifc->bAlternateSetting); 434 continue; 435 } 436 437 auto& ep_tgt = (usb_ep_direction(ep_desc) == USB_ENDPOINT_OUT) 438 ? info.out_ep 439 : info.in_ep; 440 const char* log_tag = (usb_ep_direction(ep_desc) == USB_ENDPOINT_OUT) 441 ? "output" : "input"; 442 443 // If we have already found an endpoint for this interface, log a 444 // warning and skip this one. 445 if (ep_tgt != nullptr) { 446 LOG(WARN, 447 "Multiple %s endpoints found found for MIDI streaming interface " 448 "(iid %u, alt %u, exiting ep_addr 0x%02x, new ep_addr 0x%02x)\n", 449 log_tag, 450 info.ifc->bInterfaceNumber, 451 info.ifc->bAlternateSetting, 452 ep_tgt->bEndpointAddress, 453 ep_desc->bEndpointAddress); 454 continue; 455 } 456 457 // Stash this endpoint as the found endpoint and keep parsing to 458 // consume the rest of the descriptors associated with this 459 // interface that we plan to ignore. 460 LOG(SPEW, "Found %s MIDI endpoint descriptor (addr 0x%02x, attr 0x%02x)\n", 461 log_tag, ep_desc->bEndpointAddress, ep_desc->bmAttributes); 462 ep_tgt = ep_desc; 463 } break; 464 465 case USB_AUDIO_CS_ENDPOINT: { 466 auto ep_desc = iter->hdr_as<usb_midi_ms_endpoint_desc>(); 467 if (ep_desc == nullptr) { 468 return; 469 } 470 471 if (ep_desc->bDescriptorSubtype == USB_MIDI_MS_GENERAL) { 472 LOG(SPEW, "Skipping class specific MIDI endpoint\n"); 473 continue; 474 } 475 476 return; 477 } break; 478 479 default: 480 return; 481 } 482 } 483} 484 485void UsbAudioDevice::DdkUnbind() { 486 // Unpublish our device node. 487 DdkRemove(); 488} 489 490void UsbAudioDevice::DdkRelease() { 491 // Recover our reference from the unmanaged C DDK. Then, just let it go out 492 // of scope. 493 auto reference = fbl::internal::MakeRefPtrNoAdopt(this); 494} 495 496} // namespace usb 497} // namespace audio 498 499extern "C" { 500zx_status_t usb_audio_device_bind(void* ctx, zx_device_t* device) { 501 return audio::usb::UsbAudioDevice::DriverBind(device); 502} 503 504void usb_audio_driver_release(void*) { 505 dispatcher::ThreadPool::ShutdownAll(); 506} 507} // extern "C" 508