blob: 8cc1d6a69ffa4738b9f959c98d619eb80e7e9b88 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <fbl/intrusive_double_list.h>
#include <fbl/intrusive_wavl_tree.h>
#include <fbl/unique_ptr.h>
#include <zircon/hw/usb/audio.h>
#include "usb-audio-descriptors.h"
#include "usb-audio-path.h"
#include "usb-audio-units.h"
namespace audio {
namespace usb {
class UsbAudioDevice;
class UsbAudioControlInterface {
public:
// Note that UsbAudioControlInterfaces are entirely owned by UsbAudioDevice
// instances. The control interface needs to hold a pointer to its parent,
// so it is critically important that the owning parent is certain that the
// control interface (and all of its children) have been properly shut down
// before exiting. At all times, the lifetime of the control interface
// needs to be a subset of the lifetime of the device parent.
static fbl::unique_ptr<UsbAudioControlInterface> Create(UsbAudioDevice* parent);
zx_status_t Initialize(DescriptorListMemory::Iterator* iter);
const char* log_prefix() const;
// Extract the AudioPath whose streaming terminal interface link ID matches
// the users request, if any. Otherwise, simply return nullptr.
fbl::unique_ptr<AudioPath> ExtractPath(uint8_t term_link, Direction direction) {
return paths_.erase_if(
[term_link, direction](const AudioPath& path) -> bool {
return (path.stream_terminal().id() == term_link) &&
(path.direction() == direction);
});
}
private:
friend class fbl::unique_ptr<UsbAudioControlInterface>;
using UnitMap = fbl::WAVLTree<uint32_t, fbl::RefPtr<AudioUnit>>;
UsbAudioControlInterface(UsbAudioDevice* parent);
~UsbAudioControlInterface();
// A recursive method used to find interesting audio paths in the
// unit/terminal graph. See the comment at the end of the Init
// implementation for details about the algorithm used to find these paths.
fbl::unique_ptr<AudioPath> TracePath(const OutputTerminal& out_term,
const UnitMap::iterator& current,
uint32_t level = 0);
// The reference to our parent. Note, because of this unmanaged reference,
// it is critically important that the surrounding code ensure that we never
// outlive our parent device.
UsbAudioDevice& parent_;
// Cached, unmanaged pointers to our interface and class descriptors. The
// memory which backs these descriptors is kept alive by the top level
// desc_list_ reference.
//
// TODO(johngro) : this desc_list_ memory is contained in our parent
// UsbAudioDevice. Since we have already committed to having a lifetime
// which is strictly <= the lifetime of our parent, we should probably just
// access the descriptor memory using our parent instead of holding our own
// reference to it.
fbl::RefPtr<DescriptorListMemory> desc_list_;
const usb_interface_descriptor_t* interface_hdr_ = nullptr;
const usb_audio_ac_header_desc* class_hdr_ = nullptr;
// All unit/terminal IDs for a given Audio Control Interface
//
// TODO(johngro): Strictly speaking, we don't really need to hold onto this.
// If we wanted, we could just keep this as a local variable used during
// Init and discard it afterwards.
UnitMap units_;
// The complete set of valid paths we have discovered.
fbl::DoublyLinkedList<fbl::unique_ptr<AudioPath>> paths_;
};
} // namespace usb
} // namespace audio