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 #pragma once
6 
7 #include <ddk/device.h>
8 #include <ddk/driver.h>
9 #include <ddktl/device-internal.h>
10 #include <type_traits>
11 #include <zircon/assert.h>
12 
13 // ddk::Device<D, ...>
14 //
15 // Notes:
16 //
17 // ddk::Device<D, ...> is a mixin class that simplifies writing DDK drivers in
18 // C++. The DDK's zx_device_t defines a set of function pointer callbacks that
19 // can be implemented to define standard behavior (e.g., open/close/read/write),
20 // as well as to implement device lifecycle events (e.g., unbind/release). The
21 // mixin classes are used to set up the function pointer table to call methods
22 // from the user's class automatically.
23 //
24 // Every ddk::Device subclass must implement the following release callback to
25 // cleanup resources:
26 //
27 // void DdkRelease();
28 //
29 //
30 // :: Available mixins ::
31 // +----------------------+----------------------------------------------------+
32 // | Mixin class          | Required function implementation                   |
33 // +----------------------+----------------------------------------------------+
34 // | ddk::GetProtocolable | zx_status_t DdkGetProtocol(uint32_t proto_id,      |
35 // |                      |                            void* out)              |
36 // |                      |                                                    |
37 // | ddk::Openable        | zx_status_t DdkOpen(zx_device_t** dev_out,         |
38 // |                      |                     uint32_t flags)                |
39 // |                      |                                                    |
40 // | ddk::OpenAtable      | zx_status_t DdkOpenAt(zx_device_t** dev_out,       |
41 // |                      |                       const char* path,            |
42 // |                      |                       uint32_t flags)              |
43 // |                      |                                                    |
44 // | ddk::Closable        | zx_status_t DdkClose(uint32_t flags)               |
45 // |                      |                                                    |
46 // | ddk::Unbindable      | void DdkUnbind()                                   |
47 // |                      |                                                    |
48 // | ddk::Readable        | zx_status_t DdkRead(void* buf, size_t count,       |
49 // |                      |                     zx_off_t off, size_t* actual)  |
50 // |                      |                                                    |
51 // | ddk::Writable        | zx_status_t DdkWrite(const void* buf,              |
52 // |                      |                      size_t count, zx_off_t off,   |
53 // |                      |                      size_t* actual)               |
54 // |                      |                                                    |
55 // | ddk::GetSizable      | zx_off_t DdkGetSize()                              |
56 // |                      |                                                    |
57 // | ddk::Ioctlable       | zx_status_t DdkIoctl(uint32_t op,                  |
58 // |                      |                      const void* in_buf,           |
59 // |                      |                      size_t in_len, void* out_buf, |
60 // |                      |                      size_t out_len,               |
61 // |                      |                      size_t* actual)               |
62 // |                      |                                                    |
63 // | ddk::Messageable     | zx_status_t DdkMessage(fidl_msg_t* msg,            |
64 // |                      |                        fidl_txn_t* txn)            |
65 // |                      |                                                    |
66 // | ddk::Suspendable     | zx_status_t DdkSuspend(uint32_t flags)             |
67 // |                      |                                                    |
68 // | ddk::Resumable       | zx_status_t DdkResume(uint32_t flags)              |
69 // |                      |                                                    |
70 // | ddk::Rxrpcable       | zx_status_t DdkRxrpc(zx_handle_t channel)          |
71 // +----------------------+----------------------------------------------------+
72 //
73 // Note: the ddk::FullDevice type alias may also be used if your device class
74 // will implement every mixin.
75 //
76 //
77 // :: Example ::
78 //
79 // // Define our device type using a type alias.
80 // class MyDevice;
81 // using DeviceType = ddk::Device<MyDevice, ddk::Openable, ddk::Closable,
82 //                                          ddk::Readable, ddk::Unbindable>;
83 //
84 // class MyDevice : public DeviceType {
85 //   public:
86 //     MyDevice(zx_device_t* parent)
87 //       : DeviceType(parent) {}
88 //
89 //     zx_status_t Bind() {
90 //         // Any other setup required by MyDevice. The device_add_args_t will be filled out by the
91 //         // base class.
92 //         return DdkAdd("my-device-name");
93 //     }
94 //
95 //     // Methods required by the ddk mixins
96 //     zx_status_t DdkOpen(zx_device_t** dev_out, uint32_t flags);
97 //     zx_status_t DdkClose(uint32_t flags);
98 //     zx_status_t DdkRead(void* buf, size_t count, zx_off_t off, size_t* actual);
99 //     void DdkUnbind();
100 //     void DdkRelease();
101 // };
102 //
103 // extern "C" zx_status_t my_bind(zx_device_t* device,
104 //                                void** cookie) {
105 //     auto dev = make_unique<MyDevice>(device);
106 //     auto status = dev->Bind();
107 //     if (status == ZX_OK) {
108 //         // devmgr is now in charge of the memory for dev
109 //         dev.release();
110 //     }
111 //     return status;
112 // }
113 //
114 // See also: protocol mixins for setting protocol_id and protocol_ops.
115 
116 namespace ddk {
117 
118 struct AnyProtocol {
119     void* ops;
120     void* ctx;
121 };
122 
123 // base_mixin is a tag that all mixins must inherit from.
124 using base_mixin = internal::base_mixin;
125 
126 // base_protocol is a tag used by protocol implementations
127 using base_protocol = internal::base_protocol;
128 
129 // DDK Device mixins
130 
131 template <typename D>
132 class GetProtocolable : public base_mixin {
133   protected:
GetProtocolable(zx_protocol_device_t * proto)134     explicit GetProtocolable(zx_protocol_device_t* proto) {
135         internal::CheckGetProtocolable<D>();
136         proto->get_protocol = GetProtocol;
137     }
138 
139   private:
GetProtocol(void * ctx,uint32_t proto_id,void * out)140     static zx_status_t GetProtocol(void* ctx, uint32_t proto_id, void* out) {
141         return static_cast<D*>(ctx)->DdkGetProtocol(proto_id, out);
142     }
143 };
144 
145 template <typename D>
146 class Openable : public base_mixin {
147   protected:
Openable(zx_protocol_device_t * proto)148     explicit Openable(zx_protocol_device_t* proto) {
149         internal::CheckOpenable<D>();
150         proto->open = Open;
151     }
152 
153   private:
Open(void * ctx,zx_device_t ** dev_out,uint32_t flags)154     static zx_status_t Open(void* ctx, zx_device_t** dev_out, uint32_t flags) {
155         return static_cast<D*>(ctx)->DdkOpen(dev_out, flags);
156     }
157 };
158 
159 template <typename D>
160 class OpenAtable : public base_mixin {
161   protected:
OpenAtable(zx_protocol_device_t * proto)162     explicit OpenAtable(zx_protocol_device_t* proto) {
163         internal::CheckOpenAtable<D>();
164         proto->open_at = OpenAt;
165     }
166 
167   private:
OpenAt(void * ctx,zx_device_t ** dev_out,const char * path,uint32_t flags)168     static zx_status_t OpenAt(void* ctx, zx_device_t** dev_out, const char* path,
169                               uint32_t flags) {
170         return static_cast<D*>(ctx)->DdkOpenAt(dev_out, path, flags);
171     }
172 };
173 
174 template <typename D>
175 class Closable : public base_mixin {
176   protected:
Closable(zx_protocol_device_t * proto)177     explicit Closable(zx_protocol_device_t* proto) {
178         internal::CheckClosable<D>();
179         proto->close = Close;
180     }
181 
182   private:
Close(void * ctx,uint32_t flags)183     static zx_status_t Close(void* ctx, uint32_t flags) {
184         return static_cast<D*>(ctx)->DdkClose(flags);
185     }
186 };
187 
188 template <typename D>
189 class Unbindable : public base_mixin {
190   protected:
Unbindable(zx_protocol_device_t * proto)191     explicit Unbindable(zx_protocol_device_t* proto) {
192         internal::CheckUnbindable<D>();
193         proto->unbind = Unbind;
194     }
195 
196   private:
Unbind(void * ctx)197     static void Unbind(void* ctx) {
198         static_cast<D*>(ctx)->DdkUnbind();
199     }
200 };
201 
202 template <typename D>
203 class Readable : public base_mixin {
204   protected:
Readable(zx_protocol_device_t * proto)205     explicit Readable(zx_protocol_device_t* proto) {
206         internal::CheckReadable<D>();
207         proto->read = Read;
208     }
209 
210   private:
Read(void * ctx,void * buf,size_t count,zx_off_t off,size_t * actual)211     static zx_status_t Read(void* ctx, void* buf, size_t count, zx_off_t off, size_t* actual) {
212         return static_cast<D*>(ctx)->DdkRead(buf, count, off, actual);
213     }
214 };
215 
216 template <typename D>
217 class Writable : public base_mixin {
218   protected:
Writable(zx_protocol_device_t * proto)219     explicit Writable(zx_protocol_device_t* proto) {
220         internal::CheckWritable<D>();
221         proto->write = Write;
222     }
223 
224   private:
Write(void * ctx,const void * buf,size_t count,zx_off_t off,size_t * actual)225     static zx_status_t Write(void* ctx, const void* buf, size_t count, zx_off_t off,
226                              size_t* actual) {
227         return static_cast<D*>(ctx)->DdkWrite(buf, count, off, actual);
228     }
229 };
230 
231 template <typename D>
232 class GetSizable : public base_mixin {
233   protected:
GetSizable(zx_protocol_device_t * proto)234     explicit GetSizable(zx_protocol_device_t* proto) {
235         internal::CheckGetSizable<D>();
236         proto->get_size = GetSize;
237     }
238 
239   private:
GetSize(void * ctx)240     static zx_off_t GetSize(void* ctx) {
241         return static_cast<D*>(ctx)->DdkGetSize();
242     }
243 };
244 
245 template <typename D>
246 class Ioctlable : public base_mixin {
247   protected:
Ioctlable(zx_protocol_device_t * proto)248     explicit Ioctlable(zx_protocol_device_t* proto) {
249         internal::CheckIoctlable<D>();
250         proto->ioctl = Ioctl;
251     }
252 
253   private:
Ioctl(void * ctx,uint32_t op,const void * in_buf,size_t in_len,void * out_buf,size_t out_len,size_t * out_actual)254     static zx_status_t Ioctl(void* ctx, uint32_t op, const void* in_buf, size_t in_len,
255                              void* out_buf, size_t out_len, size_t* out_actual) {
256         return static_cast<D*>(ctx)->DdkIoctl(op, in_buf, in_len, out_buf, out_len, out_actual);
257     }
258 };
259 
260 template <typename D>
261 class Messageable : public base_mixin {
262   protected:
Messageable(zx_protocol_device_t * proto)263     explicit Messageable(zx_protocol_device_t* proto) {
264         internal::CheckMessageable<D>();
265         proto->message = Message;
266     }
267 
268   private:
Message(void * ctx,fidl_msg_t * msg,fidl_txn_t * txn)269     static zx_status_t Message(void* ctx, fidl_msg_t* msg, fidl_txn_t* txn) {
270         return static_cast<D*>(ctx)->DdkMessage(msg, txn);
271     }
272 };
273 
274 template <typename D>
275 class Suspendable : public base_mixin {
276   protected:
Suspendable(zx_protocol_device_t * proto)277     explicit Suspendable(zx_protocol_device_t* proto) {
278         internal::CheckSuspendable<D>();
279         proto->suspend = Suspend;
280     }
281 
282   private:
Suspend(void * ctx,uint32_t flags)283     static zx_status_t Suspend(void* ctx, uint32_t flags) {
284         return static_cast<D*>(ctx)->DdkSuspend(flags);
285     }
286 };
287 
288 template <typename D>
289 class Resumable : public base_mixin {
290   protected:
Resumable(zx_protocol_device_t * proto)291     explicit Resumable(zx_protocol_device_t* proto) {
292         internal::CheckResumable<D>();
293         proto->resume = Resume;
294     }
295 
296   private:
Resume(void * ctx,uint32_t flags)297     static zx_status_t Resume(void* ctx, uint32_t flags) {
298         return static_cast<D*>(ctx)->DdkResume(flags);
299     }
300 };
301 
302 template <typename D>
303 class Rxrpcable : public base_mixin {
304   protected:
Rxrpcable(zx_protocol_device_t * proto)305     explicit Rxrpcable(zx_protocol_device_t* proto) {
306         internal::CheckRxrpcable<D>();
307         proto->rxrpc = Rxrpc;
308     }
309 
310   private:
Rxrpc(void * ctx,zx_handle_t channel)311     static zx_status_t Rxrpc(void* ctx, zx_handle_t channel) {
312         return static_cast<D*>(ctx)->DdkRxrpc(channel);
313     }
314 };
315 
316 // Device is templated on the list of mixins that define which DDK device
317 // methods are implemented. Note that internal::base_device *must* be the
318 // left-most base class in order to ensure that its constructor runs before the
319 // mixin constructors. This ensures that ddk_device_proto_ is zero-initialized
320 // before setting the fields in the mixins.
321 template <class D, template <typename> class... Mixins>
322 class Device : public ::ddk::internal::base_device, public Mixins<D>... {
323   public:
324     zx_status_t DdkAdd(const char* name, uint32_t flags = 0, zx_device_prop_t* props = nullptr,
325                        uint32_t prop_count = 0, uint32_t proto_id = 0,
326                        const char* proxy_args = nullptr) {
327         if (zxdev_ != nullptr) {
328             return ZX_ERR_BAD_STATE;
329         }
330 
331         device_add_args_t args = {};
332         args.version = DEVICE_ADD_ARGS_VERSION;
333         args.name = name;
334         // Since we are stashing this as a D*, we can use ctx in all
335         // the callback functions and cast it directly to a D*.
336         args.ctx = static_cast<D*>(this);
337         args.ops = &ddk_device_proto_;
338         args.flags = flags;
339         args.props = props;
340         args.prop_count = prop_count;
341         args.proto_id = proto_id;
342         args.proxy_args = proxy_args;
343         AddProtocol(&args);
344 
345         return device_add(parent_, &args, &zxdev_);
346     }
347 
DdkMakeVisible()348     void DdkMakeVisible() {
349         device_make_visible(zxdev());
350     }
351 
352     // Removes the device.
353     // This method may have the side-effect of destroying this object if the
354     // device's reference count drops to zero.
DdkRemove()355     zx_status_t DdkRemove() {
356         if (zxdev_ == nullptr) {
357             return ZX_ERR_BAD_STATE;
358         }
359 
360         // The call to |device_remove| must be last since it decrements the
361         // device's reference count when successful.
362         zx_device_t* dev = zxdev_;
363         zxdev_ = nullptr;
364         return device_remove(dev);
365     }
366 
DdkGetMetadata(uint32_t type,void * buf,size_t buf_len,size_t * actual)367     zx_status_t DdkGetMetadata(uint32_t type, void* buf, size_t buf_len, size_t* actual) {
368         return device_get_metadata(zxdev(), type, buf, buf_len, actual);
369     }
370 
DdkAddMetadata(uint32_t type,const void * data,size_t length)371     zx_status_t DdkAddMetadata(uint32_t type, const void* data, size_t length) {
372         return device_add_metadata(zxdev(), type, data, length);
373     }
374 
DdkPublishMetadata(const char * path,uint32_t type,const void * data,size_t length)375     zx_status_t DdkPublishMetadata(const char* path, uint32_t type, const void* data,
376                                    size_t length) {
377         return device_publish_metadata(zxdev(), path, type, data, length);
378     }
379 
name()380     const char* name() const { return zxdev() ? device_get_name(zxdev()) : nullptr; }
381 
382     // The opaque pointer representing this device.
zxdev()383     zx_device_t* zxdev() const { return zxdev_; }
384     // The opaque pointer representing the device's parent.
parent()385     zx_device_t* parent() const { return parent_; }
386 
SetState(zx_signals_t stateflag)387     void SetState(zx_signals_t stateflag) {
388         device_state_set(zxdev_, stateflag);
389     }
390 
ClearState(zx_signals_t stateflag)391     void ClearState(zx_signals_t stateflag) {
392         device_state_clr(zxdev_, stateflag);
393     }
394 
ClearAndSetState(zx_signals_t clearflag,zx_signals_t setflag)395     void ClearAndSetState(zx_signals_t clearflag, zx_signals_t setflag) {
396         device_state_clr_set(zxdev_, clearflag, setflag);
397     }
398 
399   protected:
Device(zx_device_t * parent)400     Device(zx_device_t* parent)
401       : internal::base_device(parent),
402         Mixins<D>(&ddk_device_proto_)... {
403         internal::CheckMixins<Mixins<D>...>();
404         internal::CheckReleasable<D>();
405 
406         ddk_device_proto_.release = DdkReleaseThunk;
407     }
408 
409   private:
DdkReleaseThunk(void * ctx)410     static void DdkReleaseThunk(void* ctx) {
411         static_cast<D*>(ctx)->DdkRelease();
412     }
413 
414     // Add the protocol id and ops if D inherits from a base_protocol implementation.
415     template <typename T = D>
416     void AddProtocol(device_add_args_t* args,
417                      typename std::enable_if<internal::is_base_protocol<T>::value, T>::type* dummy = 0) {
418         auto dev = static_cast<D*>(this);
419         ZX_ASSERT(dev->ddk_proto_id_ > 0);
420         args->proto_id = dev->ddk_proto_id_;
421         args->proto_ops = dev->ddk_proto_ops_;
422     }
423 
424     // If D does not inherit from a base_protocol implementation, do nothing.
425     template <typename T = D>
426     void AddProtocol(device_add_args_t* args,
427                      typename std::enable_if<!internal::is_base_protocol<T>::value, T>::type* dummy = 0) {}
428 };
429 
430 // Convenience type for implementations that would like to override all
431 // zx_protocol_device_t methods.
432 template <class D>
433 using FullDevice = Device<D,
434                           GetProtocolable,
435                           Openable,
436                           OpenAtable,
437                           Closable,
438                           Unbindable,
439                           Readable,
440                           Writable,
441                           GetSizable,
442                           Ioctlable,
443                           Suspendable,
444                           Resumable,
445                           Rxrpcable>;
446 
447 }  // namespace ddk
448