blob: 12b431c558321b412a3e586b50395105c21a112b [file] [log] [blame]
// 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 "zircon_device.h"
#include <dirent.h>
#include <fidl/fuchsia.hardware.intel.hda/cpp/wire.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fidl/cpp/wire/client.h>
#include <stdio.h>
#include <zircon/device/intel-hda.h>
#include <limits>
#include <fbl/unique_fd.h>
namespace audio {
namespace intel_hda {
namespace {
// Wraps a call to ComponentController.GetChannel or CodecController.GetChannel.
template <typename Protocol>
zx::result<zx::channel> GetChannel(const char* device_path) {
auto channel = component::Connect<Protocol>(device_path);
if (!channel.is_ok()) {
printf("[%s] Failed to connect to device channel (%d)\n", device_path, channel.status_value());
return channel.take_error();
}
auto result = fidl::WireCall(*channel)->GetChannel();
if (!result.ok()) {
printf("[%s] Failed to fetch device channel (%d)\n", device_path, result.status());
return zx::error(result.status());
}
return zx::ok(std::move(result->ch));
}
} // namespace
uint32_t ZirconDevice::transaction_id_ = 0;
zx_status_t ZirconDevice::Connect() {
if (dev_channel_ != ZX_HANDLE_INVALID) {
return ZX_OK;
}
if (!dev_name_) {
return ZX_ERR_NO_MEMORY;
}
zx::result<zx::channel> dev_channel;
switch (type_) {
case Type::Controller:
dev_channel = GetChannel<fuchsia_hardware_intel_hda::ControllerDevice>(dev_name_);
break;
case Type::Codec:
dev_channel = GetChannel<fuchsia_hardware_intel_hda::ControllerDevice>(dev_name_);
break;
default:
return ZX_ERR_INTERNAL;
}
if (!dev_channel.is_ok()) {
return dev_channel.status_value();
}
dev_channel_ = std::move(dev_channel.value());
return ZX_OK;
}
void ZirconDevice::Disconnect() { dev_channel_.reset(); }
zx_status_t ZirconDevice::CallDevice(const zx_channel_call_args_t& args, zx::duration timeout) {
uint32_t resp_size;
uint32_t resp_handles;
zx::time deadline =
timeout == zx::duration::infinite() ? zx::time::infinite() : zx::deadline_after(timeout);
return dev_channel_.call(0, deadline, &args, &resp_size, &resp_handles);
}
zx_status_t ZirconDevice::Enumerate(void* ctx, const char* const dev_path, EnumerateCbk cbk) {
static constexpr size_t FILENAME_SIZE = 256;
struct dirent* de;
DIR* dir = opendir(dev_path);
zx_status_t res = ZX_OK;
char buf[FILENAME_SIZE];
if (!dir)
return ZX_ERR_NOT_FOUND;
while ((de = readdir(dir)) != NULL) {
uint32_t id;
if (sscanf(de->d_name, "%u", &id) == 1) {
size_t total = 0;
total += snprintf(buf + total, sizeof(buf) - total, "%s/", dev_path);
total += snprintf(buf + total, sizeof(buf) - total, "%03u", id);
res = cbk(ctx, id, buf);
if (res != ZX_OK)
goto done;
}
}
done:
closedir(dir);
return res;
}
} // namespace intel_hda
} // namespace audio