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