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