1// Copyright 2017 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 <dirent.h>
6#include <fcntl.h>
7#include <stdio.h>
8
9#include <zircon/device/intel-hda.h>
10#include <lib/fdio/io.h>
11#include <fbl/limits.h>
12
13#include "zircon_device.h"
14
15namespace audio {
16namespace intel_hda {
17
18uint32_t ZirconDevice::transaction_id_ = 0;
19
20zx_status_t ZirconDevice::Connect() {
21    if (dev_channel_ != ZX_HANDLE_INVALID)
22        return ZX_OK;
23
24    if (!dev_name_)
25        return ZX_ERR_NO_MEMORY;
26
27    int fd = ::open(dev_name_, O_RDONLY);
28    if (fd < 0)
29        return static_cast<zx_status_t>(fd);
30
31    ssize_t res = ::fdio_ioctl(fd, IHDA_IOCTL_GET_CHANNEL,
32                               nullptr, 0,
33                               &dev_channel_, sizeof(dev_channel_));
34    ::close(fd);
35
36    if (res < 0) {
37        printf("[%s] Failed to fetch device channel (%zd)\n", dev_name(), res);
38        return static_cast<zx_status_t>(res);
39    }
40
41    return ZX_OK;
42}
43
44void ZirconDevice::Disconnect() {
45    if (dev_channel_ != ZX_HANDLE_INVALID) {
46        ::zx_handle_close(dev_channel_);
47        dev_channel_ = ZX_HANDLE_INVALID;
48    }
49}
50
51zx_status_t ZirconDevice::CallDevice(const zx_channel_call_args_t& args, uint64_t timeout_msec) {
52    uint32_t resp_size;
53    uint32_t resp_handles;
54    zx_time_t deadline;
55
56    if (timeout_msec == ZX_TIME_INFINITE) {
57        deadline = ZX_TIME_INFINITE;
58    } else if (timeout_msec >= fbl::numeric_limits<zx_time_t>::max() / ZX_MSEC(1)) {
59        return ZX_ERR_INVALID_ARGS;
60    } else {
61        deadline = zx_deadline_after(ZX_MSEC(timeout_msec));
62    }
63
64    return zx_channel_call(dev_channel_, 0, deadline, &args, &resp_size, &resp_handles);
65}
66
67zx_status_t ZirconDevice::Enumerate(
68        void* ctx,
69        const char* const dev_path,
70        EnumerateCbk cbk) {
71    static constexpr size_t FILENAME_SIZE = 256;
72
73    struct dirent* de;
74    DIR* dir = opendir(dev_path);
75    zx_status_t res = ZX_OK;
76    char buf[FILENAME_SIZE];
77
78    if (!dir)
79        return ZX_ERR_NOT_FOUND;
80
81    while ((de = readdir(dir)) != NULL) {
82        uint32_t id;
83        if (sscanf(de->d_name, "%u", &id) == 1) {
84            size_t total = 0;
85
86            total += snprintf(buf + total, sizeof(buf) - total, "%s/", dev_path);
87            total += snprintf(buf + total, sizeof(buf) - total, "%03u", id);
88
89            res = cbk(ctx, id, buf);
90            if (res != ZX_OK)
91                goto done;
92        }
93    }
94
95done:
96    closedir(dir);
97    return res;
98}
99
100}  // namespace audio
101}  // namespace intel_hda
102