| // Copyright 2017 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. |
| |
| #include "src/media/audio/audio_core/audio_plug_detector.h" |
| |
| #include <dirent.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/macros.h> |
| #include <fbl/unique_fd.h> |
| #include <fcntl.h> |
| #include <fuchsia/hardware/audio/cpp/fidl.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/fit/defer.h> |
| #include <lib/zx/channel.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <zircon/compiler.h> |
| #include <zircon/device/audio.h> |
| #include <zircon/device/vfs.h> |
| |
| #include "src/lib/files/unique_fd.h" |
| #include "src/media/audio/audio_core/audio_device_manager.h" |
| #include "src/media/audio/audio_core/audio_input.h" |
| #include "src/media/audio/audio_core/audio_output.h" |
| #include "src/media/audio/audio_core/driver_output.h" |
| |
| namespace media::audio { |
| |
| static const struct { |
| const char* path; |
| bool is_input; |
| } AUDIO_DEVNODES[] = { |
| {.path = "/dev/class/audio-output", .is_input = false}, |
| {.path = "/dev/class/audio-input", .is_input = true}, |
| }; |
| |
| AudioPlugDetector::~AudioPlugDetector() { FXL_DCHECK(manager_ == nullptr); } |
| |
| zx_status_t AudioPlugDetector::Start(AudioDeviceManager* manager) { |
| FXL_DCHECK(manager != nullptr); |
| |
| // If we fail to set up monitoring for any of our target directories, |
| // automatically stop monitoring all sources of device nodes. |
| auto error_cleanup = fit::defer([this]() { Stop(); }); |
| |
| // If we are already running, we cannot start again. Cancel the cleanup |
| // operation and report that things are successfully started. |
| if (manager_ != nullptr) { |
| FXL_LOG(INFO) << "Attempted to start the AudioPlugDetector twice!"; |
| error_cleanup.cancel(); |
| return ZX_OK; |
| } |
| |
| // Record our new manager |
| manager_ = manager; |
| |
| // Create our watchers. |
| for (const auto& devnode : AUDIO_DEVNODES) { |
| auto watcher = fsl::DeviceWatcher::Create( |
| devnode.path, [this, is_input = devnode.is_input]( |
| int dir_fd, const std::string& filename) { |
| AddAudioDevice(dir_fd, filename, is_input); |
| }); |
| |
| if (watcher == nullptr) { |
| FXL_LOG(ERROR) |
| << "AudioPlugDetector failed to create DeviceWatcher for \"" |
| << devnode.path << "\"."; |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| watchers_.emplace_back(std::move(watcher)); |
| } |
| |
| error_cleanup.cancel(); |
| |
| return ZX_OK; |
| } |
| |
| void AudioPlugDetector::Stop() { |
| manager_ = nullptr; |
| watchers_.clear(); |
| } |
| |
| void AudioPlugDetector::AddAudioDevice(int dir_fd, const std::string& name, |
| bool is_input) { |
| if (manager_ == nullptr) |
| return; |
| |
| // Open the device node. |
| fbl::unique_fd dev_node(::openat(dir_fd, name.c_str(), O_RDONLY)); |
| if (!dev_node.is_valid()) { |
| FXL_LOG(WARNING) << "AudioPlugDetector failed to open device node at \"" |
| << name << "\". (" << strerror(errno) << " : " << errno |
| << ")"; |
| return; |
| } |
| |
| // Obtain the FDIO device channel, then wrap it in a synchronous proxy and use |
| // it to get the stream channel. |
| zx_status_t res; |
| zx::channel dev_channel; |
| res = fdio_get_service_handle(dev_node.release(), |
| dev_channel.reset_and_get_address()); |
| if (res != ZX_OK) { |
| FXL_LOG(ERROR) << "Failed to obtain FDIO service channel to AudioOutput " |
| << "(res " << res << ")"; |
| return; |
| } |
| |
| // Obtain the stream channel |
| zx::channel channel; |
| ::fuchsia::hardware::audio::Device_SyncProxy dev(std::move(dev_channel)); |
| |
| res = dev.GetChannel(&channel); |
| if (res != ZX_OK) { |
| FXL_LOG(ERROR) << "Failed to open channel to AudioOutput (res " << res |
| << ")"; |
| return; |
| } |
| |
| // Hand the stream off to the proper type of class to manage. |
| fbl::RefPtr<AudioDevice> new_device; |
| if (is_input) { |
| new_device = AudioInput::Create(std::move(channel), manager_); |
| } else { |
| new_device = DriverOutput::Create(std::move(channel), manager_); |
| } |
| |
| if (new_device == nullptr) { |
| FXL_LOG(WARNING) << "Failed to instantiate audio " |
| << (is_input ? "input" : "output") << " for \"" << name |
| << "\""; |
| } else { |
| manager_->AddDevice(std::move(new_device)); |
| } |
| } |
| |
| } // namespace media::audio |