1// Copyright 2016 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 <zircon/compiler.h>
6
7#include <ddk/debug.h>
8#include <ddk/device.h>
9#include "devhost.h"
10
11#include <stdarg.h>
12#include <stdio.h>
13
14// These are the API entry-points from drivers
15// They must take the devhost_api_lock before calling devhost_* internals
16//
17// Driver code MUST NOT directly call devhost_* APIs
18
19
20// LibDriver Device Interface
21
22#define ALLOWED_FLAGS (\
23    DEVICE_ADD_NON_BINDABLE | DEVICE_ADD_INSTANCE |\
24    DEVICE_ADD_MUST_ISOLATE | DEVICE_ADD_INVISIBLE)
25
26__EXPORT zx_status_t device_add_from_driver(zx_driver_t* drv, zx_device_t* parent,
27                                            device_add_args_t* args, zx_device_t** out) {
28    zx_status_t r;
29    zx_device_t* dev = nullptr;
30
31    if (!parent) {
32        return ZX_ERR_INVALID_ARGS;
33    }
34    if (!args || args->version != DEVICE_ADD_ARGS_VERSION) {
35        return ZX_ERR_INVALID_ARGS;
36    }
37    if (!args->ops || args->ops->version != DEVICE_OPS_VERSION) {
38        return ZX_ERR_INVALID_ARGS;
39    }
40    if (args->flags & ~ALLOWED_FLAGS) {
41        return ZX_ERR_INVALID_ARGS;
42    }
43    if ((args->flags & DEVICE_ADD_INSTANCE) &&
44        (args->flags & (DEVICE_ADD_MUST_ISOLATE | DEVICE_ADD_INVISIBLE))) {
45        return ZX_ERR_INVALID_ARGS;
46    }
47
48    DM_LOCK();
49    r = devhost_device_create(drv, parent, args->name, args->ctx, args->ops, &dev);
50    if (r != ZX_OK) {
51        DM_UNLOCK();
52        return r;
53    }
54    if (args->proto_id) {
55        dev->protocol_id = args->proto_id;
56        dev->protocol_ops = args->proto_ops;
57    }
58    if (args->flags & DEVICE_ADD_NON_BINDABLE) {
59        dev->flags |= DEV_FLAG_UNBINDABLE;
60    }
61    if (args->flags & DEVICE_ADD_INVISIBLE) {
62        dev->flags |= DEV_FLAG_INVISIBLE;
63    }
64
65    // out must be set before calling devhost_device_add().
66    // devhost_device_add() may result in child devices being created before it returns,
67    // and those children may call ops on the device before device_add() returns.
68    if (out) {
69        *out = dev;
70    }
71
72    if (args->flags & DEVICE_ADD_MUST_ISOLATE) {
73        r = devhost_device_add(dev, parent, args->props, args->prop_count, args->proxy_args);
74    } else if (args->flags & DEVICE_ADD_INSTANCE) {
75        dev->flags |= DEV_FLAG_INSTANCE | DEV_FLAG_UNBINDABLE;
76        r = devhost_device_add(dev, parent, nullptr, 0, nullptr);
77    } else {
78        r = devhost_device_add(dev, parent, args->props, args->prop_count, nullptr);
79    }
80    if (r != ZX_OK) {
81        if (out) {
82            *out = nullptr;
83        }
84        devhost_device_destroy(dev);
85    }
86
87    DM_UNLOCK();
88    return r;
89}
90
91__EXPORT zx_status_t device_remove(zx_device_t* dev) {
92    zx_status_t r;
93    DM_LOCK();
94    r = devhost_device_remove(dev);
95    DM_UNLOCK();
96    return r;
97}
98
99__EXPORT zx_status_t device_rebind(zx_device_t* dev) {
100    zx_status_t r;
101    DM_LOCK();
102    r = devhost_device_rebind(dev);
103    DM_UNLOCK();
104    return r;
105}
106
107__EXPORT void device_make_visible(zx_device_t* dev) {
108    DM_LOCK();
109    devhost_make_visible(dev);
110    DM_UNLOCK();
111}
112
113
114__EXPORT const char* device_get_name(zx_device_t* dev) {
115    return dev->name;
116}
117
118__EXPORT zx_device_t* device_get_parent(zx_device_t* dev) {
119    return dev->parent;
120}
121
122typedef struct {
123    void* ops;
124    void* ctx;
125} generic_protocol_t;
126
127__EXPORT zx_status_t device_get_protocol(const zx_device_t* dev, uint32_t proto_id, void* out) {
128    auto proto = static_cast<generic_protocol_t*>(out);
129    if (dev->ops->get_protocol) {
130        return dev->ops->get_protocol(dev->ctx, proto_id, out);
131    }
132    if ((proto_id == dev->protocol_id) && (dev->protocol_ops != nullptr)) {
133        proto->ops = dev->protocol_ops;
134        proto->ctx = dev->ctx;
135        return ZX_OK;
136    }
137    return ZX_ERR_NOT_SUPPORTED;
138}
139
140__EXPORT void device_state_clr_set(zx_device_t* dev, zx_signals_t clearflag, zx_signals_t setflag) {
141    zx_object_signal(dev->event, clearflag, setflag);
142}
143
144
145__EXPORT zx_off_t device_get_size(zx_device_t* dev) {
146    return dev->ops->get_size(dev->ctx);
147}
148
149__EXPORT zx_status_t device_read(zx_device_t* dev, void* buf, size_t count,
150                                 zx_off_t off, size_t* actual) {
151    return dev->ops->read(dev->ctx, buf, count, off, actual);
152}
153
154__EXPORT zx_status_t device_write(zx_device_t* dev, const void* buf, size_t count,
155                                  zx_off_t off, size_t* actual) {
156    return dev->ops->write(dev->ctx, buf, count, off, actual);
157}
158
159__EXPORT zx_status_t device_ioctl(zx_device_t* dev, uint32_t op,
160                                  const void* in_buf, size_t in_len,
161                                  void* out_buf, size_t out_len,
162                                  size_t* out_actual) {
163    return dev->ops->ioctl(dev->ctx, op, in_buf, in_len, out_buf, out_len, out_actual);
164}
165
166// LibDriver Misc Interfaces
167
168extern zx_handle_t root_resource_handle;
169
170__EXPORT zx_handle_t get_root_resource() {
171    return root_resource_handle;
172}
173
174__EXPORT zx_status_t load_firmware(zx_device_t* dev, const char* path,
175                                   zx_handle_t* fw, size_t* size) {
176    zx_status_t r;
177    DM_LOCK();
178    r = devhost_load_firmware(dev, path, fw, size);
179    DM_UNLOCK();
180    return r;
181}
182
183// Interface Used by DevHost RPC Layer
184
185zx_status_t device_bind(zx_device_t* dev, const char* drv_libname) {
186    zx_status_t r;
187    DM_LOCK();
188    r = devhost_device_bind(dev, drv_libname);
189    DM_UNLOCK();
190    return r;
191}
192
193zx_status_t device_open_at(zx_device_t* dev, zx_device_t** out, const char* path, uint32_t flags) {
194    zx_status_t r;
195    DM_LOCK();
196    r = devhost_device_open_at(dev, out, path, flags);
197    DM_UNLOCK();
198    return r;
199}
200
201zx_status_t device_close(zx_device_t* dev, uint32_t flags) {
202    zx_status_t r;
203    DM_LOCK();
204    r = devhost_device_close(dev, flags);
205    DM_UNLOCK();
206    return r;
207}
208
209__EXPORT zx_status_t device_get_metadata(zx_device_t* dev, uint32_t type, void* buf, size_t buflen,
210                                         size_t* actual) {
211    zx_status_t r;
212    DM_LOCK();
213    r = devhost_get_metadata(dev, type, buf, buflen, actual);
214    DM_UNLOCK();
215    return r;
216}
217
218__EXPORT zx_status_t device_add_metadata(zx_device_t* dev, uint32_t type, const void* data,
219                                         size_t length) {
220    zx_status_t r;
221    DM_LOCK();
222    r = devhost_add_metadata(dev, type, data, length);
223    DM_UNLOCK();
224    return r;
225}
226
227__EXPORT zx_status_t device_publish_metadata(zx_device_t* dev, const char* path, uint32_t type,
228                                             const void* data, size_t length) {
229    zx_status_t r;
230    DM_LOCK();
231    r = devhost_publish_metadata(dev, path, type, data, length);
232    DM_UNLOCK();
233    return r;
234}
235