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