| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #pragma once |
| |
| #include <ddk/device.h> |
| #include <fbl/type_support.h> |
| |
| namespace ddk { |
| namespace internal { |
| |
| // base_device is a tag that default initalizes the zx_protocol_device_t so the mixin classes |
| // can fill in the table. |
| struct base_device { |
| protected: |
| base_device(zx_device_t* parent) |
| : parent_(parent) { |
| ddk_device_proto_.version = DEVICE_OPS_VERSION; |
| } |
| |
| zx_protocol_device_t ddk_device_proto_ = {}; |
| zx_device_t* mxdev_ = nullptr; |
| zx_device_t* const parent_; |
| }; |
| |
| // base_mixin is a tag that all mixins must inherit from. |
| struct base_mixin {}; |
| |
| // base_protocol is a tag used by protocol implementations |
| struct base_protocol { |
| uint32_t ddk_proto_id_ = 0; |
| void* ddk_proto_ops_ = nullptr; |
| |
| protected: |
| base_protocol() = default; |
| }; |
| |
| // Mixin checks: ensure that a type meets the following qualifications: |
| // |
| // 1) has a method with the correct name (this makes the compiler errors a little more sane), |
| // 2) inherits from ddk::Device (by checking that it inherits from ddk::internal::base_device), and |
| // 3) has the correct method signature. |
| // |
| // Note that the 3rd requirement supersedes the first, but the static_assert doesn't even compile if |
| // the method can't be found, leading to a slightly more confusing error message. Adding the first |
| // check gives a chance to show the user a more intelligible error message. |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_get_protocol, DdkGetProtocol); |
| |
| template <typename D> |
| constexpr void CheckGetProtocolable() { |
| static_assert(has_ddk_get_protocol<D>::value, |
| "GetProtocolable classes must implement DdkGetProtocol"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "GetProtocolable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkGetProtocol), |
| zx_status_t (D::*)(uint32_t, void*)>::value, |
| "DdkGetProtocol must be a public non-static member function with signature " |
| "'zx_status_t DdkGetProtocol(uint32_t, void*)'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_open, DdkOpen); |
| |
| template <typename D> |
| constexpr void CheckOpenable() { |
| static_assert(has_ddk_open<D>::value, "Openable classes must implement DdkOpen"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "Openable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkOpen), |
| zx_status_t (D::*)(zx_device_t**, uint32_t)>::value, |
| "DdkOpen must be a public non-static member function with signature " |
| "'zx_status_t DdkOpen(zx_device_t**, uint32_t)'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_open_at, DdkOpenAt); |
| |
| template <typename D> |
| constexpr void CheckOpenAtable() { |
| static_assert(has_ddk_open_at<D>::value, |
| "OpenAtable classes must implement DdkOpenAt"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "OpenAtable classes must be derived from ddk::Device<...>."); |
| static_assert( |
| fbl::is_same<decltype(&D::DdkOpenAt), |
| zx_status_t (D::*)(zx_device_t**, const char*, uint32_t)>::value, |
| "DdkOpenAt must be a public non-static member function with signature " |
| "'zx_status_t DdkOpenAt(zx_device_t**, const char*, uint32_t)'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_close, DdkClose); |
| |
| template <typename D> |
| constexpr void CheckClosable() { |
| static_assert(has_ddk_close<D>::value, |
| "Closable classes must implement DdkClose"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "Closable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkClose), zx_status_t (D::*)(uint32_t)>::value, |
| "DdkClose must be a public non-static member function with signature " |
| "'zx_status_t DdkClose(uint32)'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_unbind, DdkUnbind); |
| |
| template <typename D> |
| constexpr void CheckUnbindable() { |
| static_assert(has_ddk_unbind<D>::value, |
| "Unbindable classes must implement DdkUnbind"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "Unbindable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkUnbind), void (D::*)(void)>::value, |
| "DdkUnbind must be a public non-static member function with signature " |
| "'void DdkUnbind()'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_release, DdkRelease); |
| |
| template <typename D> |
| constexpr void CheckReleasable() { |
| static_assert(has_ddk_release<D>::value, |
| "Releasable classes must implement DdkRelease"); |
| // No need to check is_base_of because Releasable is a property of ddk::Device itself |
| static_assert(fbl::is_same<decltype(&D::DdkRelease), void (D::*)(void)>::value, |
| "DdkRelease must be a public non-static member function with signature " |
| "'void DdkRelease()'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_read, DdkRead); |
| |
| template <typename D> |
| constexpr void CheckReadable() { |
| static_assert(has_ddk_read<D>::value, "Readable classes must implement DdkRead"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "Readable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkRead), |
| zx_status_t (D::*)(void*, size_t, zx_off_t, size_t*)>::value, |
| "DdkRead must be a public non-static member function with signature " |
| "'zx_status_t DdkRead(void*, size_t, zx_off_t, size_t*)'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_write, DdkWrite); |
| |
| template <typename D> |
| constexpr void CheckWritable() { |
| static_assert(has_ddk_write<D>::value, |
| "Writable classes must implement DdkWrite"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "Writable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkWrite), |
| zx_status_t (D::*)(const void*, size_t, zx_off_t, size_t*)>::value, |
| "DdkWrite must be a public non-static member function with signature " |
| "'zx_status_t DdkWrite(const void*, size_t, zx_off_t, size_t*)'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_iotxn_queue, DdkIotxnQueue); |
| |
| template <typename D> |
| constexpr void CheckIotxnQueueable() { |
| static_assert(has_ddk_iotxn_queue<D>::value, |
| "IotxnQueueable classes must implement DdkIotxnQueue"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "IotxnQueueable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkIotxnQueue), void (D::*)(iotxn_t*)>::value, |
| "DdkIotxnQueue must be a public non-static member function with signature " |
| "'void DdkIotxnQueue(iotxn_t*)'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_get_size, DdkGetSize); |
| |
| template <typename D> |
| constexpr void CheckGetSizable() { |
| static_assert(has_ddk_get_size<D>::value, |
| "GetSizable classes must implement DdkGetSize"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "GetSizable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkGetSize), zx_off_t (D::*)(void)>::value, |
| "DdkGetSize must be a public non-static member function with signature " |
| "'zx_off_t DdkGetSize()'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_ioctl, DdkIoctl); |
| |
| template <typename D> |
| constexpr void CheckIoctlable() { |
| static_assert(has_ddk_ioctl<D>::value, |
| "Ioctlable classes must implement DdkIoctl"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "Ioctlable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkIoctl), |
| zx_status_t (D::*)(uint32_t, const void*, size_t, |
| void*, size_t, size_t*)>::value, |
| "DdkIoctl must be a public non-static member function with signature " |
| "'zx_status_t DdkIoctl(uint32_t, const void*, size_t, void*, size_t, size_t*)'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_suspend, DdkSuspend); |
| |
| template <typename D> |
| constexpr void CheckSuspendable() { |
| static_assert(has_ddk_suspend<D>::value, |
| "Suspendable classes must implement DdkSuspend"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "Suspendable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkSuspend), zx_status_t (D::*)(uint32_t)>::value, |
| "DdkSuspend must be a public non-static member function with signature " |
| "'zx_status_t DdkSuspend(uint32_t)'."); |
| } |
| |
| DECLARE_HAS_MEMBER_FN(has_ddk_resume, DdkResume); |
| |
| template <typename D> |
| constexpr void CheckResumable() { |
| static_assert(has_ddk_resume<D>::value, |
| "Resumable classes must implement DdkResume"); |
| static_assert(fbl::is_base_of<base_device, D>::value, |
| "Resumable classes must be derived from ddk::Device<...>."); |
| static_assert(fbl::is_same<decltype(&D::DdkResume), zx_status_t (D::*)(uint32_t)>::value, |
| "DdkResume must be a public non-static member function with signature " |
| "'zx_status_t DdkResume(uint32_t)'."); |
| } |
| |
| // all_mixins |
| // |
| // Checks a list of types to ensure that all of them are ddk mixins (i.e., they inherit from the |
| // internal::base_mixin tag). |
| template <typename Base, typename...> |
| struct all_mixins : fbl::true_type {}; |
| |
| template <typename Base, typename Mixin, typename... Mixins> |
| struct all_mixins<Base, Mixin, Mixins...> |
| : fbl::integral_constant<bool, fbl::is_base_of<Base, Mixin>::value && |
| all_mixins<Base, Mixins...>::value> {}; |
| |
| template <typename... Mixins> |
| constexpr void CheckMixins() { |
| static_assert(all_mixins<base_mixin, Mixins...>::value, |
| "All mixins must be from the ddk template library"); |
| } |
| |
| } // namespace internal |
| } // namespace ddk |