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