blob: 087bbdc4348dbf5b2000f60fc071d0b2090b593b [file] [log] [blame]
// 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.
#ifndef SRC_LIB_DDKTL_INCLUDE_DDKTL_DEVICE_INTERNAL_H_
#define SRC_LIB_DDKTL_INCLUDE_DDKTL_DEVICE_INTERNAL_H_
#include <lib/ddk/device.h>
#include <lib/fdf/cpp/channel.h>
#include <string>
#include <type_traits>
#include <ddktl/fidl.h>
#include <ddktl/init-txn.h>
#include <ddktl/resume-txn.h>
#include <ddktl/suspend-txn.h>
#include <ddktl/unbind-txn.h>
namespace ddk {
namespace internal {
// Macro for defining a trait that checks if a type T has a method with the
// given name. See fbl/macros.h.
//
// Example:
//
// DDTL_INTERNAL_DECLARE_HAS_MEMBER_FN(has_bar, Bar);
// template <typename T>
// class Foo {
// static_assert(has_bar_v<T>, "Foo classes must implement Bar()!");
// lands.
// static_assert(is_same_v<decltype(&T::Bar), void (T::*)(int)>,
// "Bar must be a non-static member function with signature "
// "'void Bar(int)', and must be visible to Foo (either "
// "because it is public, or due to friendship).");
// };
#define DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN(trait_name, fn_name) \
template <typename T> \
struct trait_name { \
private: \
template <typename C> \
static std::true_type test(decltype(&C::fn_name)); \
template <typename C> \
static std::false_type test(...); \
\
public: \
static constexpr bool value = decltype(test<T>(nullptr))::value; \
}; \
template <typename T> \
static inline constexpr bool trait_name##_v = trait_name<T>::value
// Example:
//
// DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_c_str, c_str, const char* (C::*)()
// const);
#define DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(trait_name, fn_name, sig) \
template <typename T> \
struct trait_name { \
private: \
template <typename C> \
static std::true_type test(decltype(static_cast<sig>(&C::fn_name))); \
template <typename C> \
static std::false_type test(...); \
\
public: \
static constexpr bool value = decltype(test<T>(nullptr))::value; \
}; \
template <typename T> \
static inline constexpr bool trait_name##_v = trait_name<T>::value
// Implemented like protocol op mixins, but defined on all base_device since they must implement
template <typename D>
class Releasable {
public:
static constexpr void InitOp(zx_protocol_device_t* proto) { proto->release = Release; }
private:
static void Release(void* ctx) { return static_cast<D*>(ctx)->DdkRelease(); }
};
// base_device is a tag that default initializes the zx_protocol_device_t so the mixin classes
// can fill in the table.
template <class D, template <typename> class... Mixins>
class base_device : public Mixins<D>... {
protected:
explicit base_device(zx_device_t* parent) : parent_(parent) {}
// populated ops table at compile time. All Mixins must have a constexpr
// InitOp function that manipulates ops for their respective function
static const constexpr zx_protocol_device_t ddk_device_proto_ = []() {
zx_protocol_device_t ops = {};
ops.version = DEVICE_OPS_VERSION;
// Releasable Mixin
Releasable<D>::InitOp(&ops);
//
// This C++17 fold expression with the , operator means this ("iterating" at compile time):
// for (typename Mixin : Mixins<D>) Mixin::Protocol(&ops);
//
// template <typename D>
// class Openable : public base_mixin {
// public:
// static constexpr void InitOp(zx_protocol_device_t* proto) {
// internal::CheckOpenable<D>();
// proto->open = Open;
// }
// private:
// static zx_status_t Open(void* ctx, zx_device_t** dev_out, uint32_t flags) {
// return static_cast<D*>(ctx)->DdkOpen(dev_out, flags);
// }
// };
//
(Mixins<D>::InitOp(&ops), ...);
return ops;
}();
std::string name_;
zx_device_t* zxdev_ = 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;
};
template <typename T>
using is_base_protocol = std::is_base_of<internal::base_protocol, T>;
template <typename T>
using is_base_proto = std::is_same<internal::base_protocol, T>;
// Deprecation helpers: transition a DDKTL protocol interface when there are implementations outside
// of zircon without breaking any builds.
//
// Example:
// template <typename D, bool NewFooMethod=false>
// class MyProtocol : public internal::base_protocol {
// public:
// MyProtocol() {
// internal::CheckMyProtocol<D, NewMethod>();
// ops_.foo = Foo;
//
// // Can only inherit from one base_protocol implementation
// ZX_ASSERT(this->ddk_proto_ops_ == nullptr);
// ddk_proto_id_ = ZX_PROTOCOL_MY_FOO;
// ddk_proto_ops_ = &ops_;
// }
//
// private:
// DDKTL_DEPRECATED(NewFooMethod)
// static Foo(void* ctx, uint32_t options, foo_info_t* info) {
// return static_cast<D*>(ctx)->MyFoo(options);
// }
//
// DDKTL_NOTREADY(NewFooMethod)
// static Foo(void* ctx, uint32_t options, foo_info_t* info) {
// return static_cast<D*>(ctx)->MyFoo(options, info);
// }
// };
//
// // This class hasn't been updated yet, so it uses the default value for NewFooMethod and has the
// // old MyFoo method implementation.
// class MyProtocolImpl : public ddk::Device<MyProtocolImpl, /* other mixins */>,
// public ddk::MyProtocol<MyProtocolImpl> {
// public:
// zx_status_t MyFoo(uint32_t options);
// };
//
// // The implementation transitions as follows:
// class MyProtocolImpl : public ddk::Device<MyProtocolImpl, /* other mixins */>,
// public ddk::MyProtocol<MyProtocolImpl, true> {
// public:
// zx_status_t MyFoo(uint32_t options, foo_info_t* info);
// };
//
// Now the DDKTL_DEPRECATED method can be removed, along with the NewFooMethod template parameter.
// At no stage should the build be broken. These annotations may also be used to add new methods, by
// providing a no-op in the DDKTL_DEPRECATED static method.
#define DDKTL_DEPRECATED(condition) \
template <typename T = D, bool En = condition, \
typename std::enable_if<!std::integral_constant<bool, En>::value, int>::type = 0>
#define DDKTL_NOTREADY(condition) \
template <typename T = D, bool En = condition, \
typename std::enable_if<std::integral_constant<bool, En>::value, int>::type = 0>
// 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.
DDKTL_INTERNAL_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(
std::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*)'.");
}
DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN(has_ddk_init, DdkInit);
template <typename D>
constexpr void CheckInitializable() {
static_assert(has_ddk_init<D>::value, "Init classes must implement DdkInit");
static_assert(std::is_same<decltype(&D::DdkInit), void (D::*)(InitTxn txn)>::value,
"DdkInit must be a public non-static member function with signature "
"'void DdkInit(ddk::InitTxn)'.");
}
DDKTL_INTERNAL_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(
std::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)'.");
}
DDKTL_INTERNAL_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(std::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)'.");
}
DDKTL_INTERNAL_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(std::is_same<decltype(&D::DdkUnbind), void (D::*)(UnbindTxn txn)>::value,
"DdkUnbind must be a public non-static member function with signature "
"'void DdkUnbind(ddk::UnbindTxn)'.");
}
DDKTL_INTERNAL_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");
static_assert(std::is_same<decltype(&D::DdkRelease), void (D::*)(void)>::value,
"DdkRelease must be a public non-static member function with signature "
"'void DdkRelease()'.");
}
DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN(has_ddk_service_connect, DdkServiceConnect);
template <typename D>
constexpr void CheckServiceConnectable() {
static_assert(has_ddk_service_connect<D>::value,
"ServiceConnectable classes must implement DdkServiceConnect");
static_assert(std::is_same<decltype(&D::DdkServiceConnect),
zx_status_t (D::*)(const char*, fdf::Channel)>::value,
"DdkServiceConnect must be a public non-static member function with signature "
"'zx_status_t DdkServiceConnect(const char*, fdf::Channel)'.");
}
DDKTL_INTERNAL_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(std::is_same<decltype(&D::DdkSuspend), void (D::*)(SuspendTxn txn)>::value,
"DdkSuspend must be a public non-static member function with signature "
"'zx_status_t DdkSuspend(SuspendTxn txn)'.");
}
DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN(has_ddk_auto_configure_suspend, DdkConfigureAutoSuspend);
template <typename D>
constexpr void CheckConfigureAutoSuspend() {
static_assert(has_ddk_auto_configure_suspend<D>::value,
"ConfigureAutoSuspendable classes must implement DdkConfigureAutoSuspend");
static_assert(
std::is_same<decltype(&D::DdkConfigureAutoSuspend), zx_status_t (D::*)(bool, uint8_t)>::value,
"DdkConfigureAutoSuspend must be a public non-static member function with signature "
"'zx_status_t DdkConfigureAutoSuspend(bool, uint8_t)'.");
}
DDKTL_INTERNAL_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(std::is_same<decltype(&D::DdkResume), void (D::*)(ResumeTxn txn)>::value,
"DdkResume must be a public non-static member function with signature "
"'void DdkResume(ResumeTxn)'.");
}
DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN(has_ddk_set_performance_state, DdkSetPerformanceState);
template <typename D>
constexpr void CheckPerformanceTunable() {
static_assert(has_ddk_set_performance_state<D>::value,
"PerformanceTunable classes must implement DdkSetPerformanceState");
static_assert(std::is_same<decltype(&D::DdkSetPerformanceState),
zx_status_t (D::*)(uint32_t, uint32_t*)>::value,
"DdkSetPerformanceState must be a public non-static member function with signature "
"'zx_status_t DdkSetPerformanceState(uint32_t, uint32_t*)'.");
}
DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN(has_ddk_rxrpc, DdkRxrpc);
template <typename D>
constexpr void CheckRxrpcable() {
static_assert(has_ddk_rxrpc<D>::value, "Rxrpcable classes must implement DdkRxrpc");
static_assert(std::is_same<decltype(&D::DdkRxrpc), zx_status_t (D::*)(uint32_t)>::value,
"DdkRxrpc must be a public non-static member function with signature "
"'zx_status_t DdkRxrpc(zx_handle_t)'.");
}
DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN(has_ddk_child_pre_release, DdkChildPreRelease);
template <typename D>
constexpr void CheckChildPreReleaseable() {
static_assert(has_ddk_child_pre_release<D>::value,
"ChildPreReleaseable classes must implement DdkChildPreRelease");
static_assert(std::is_same<decltype(&D::DdkChildPreRelease), void (D::*)(void*)>::value,
"DdkChildPreRelease must be a public non-static member function with signature "
"'void DdkChildPreRelease(void*)'.");
}
DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN(has_ddk_open_protocol_session_multibindable,
DdkOpenProtocolSessionMultibindable);
DDKTL_INTERNAL_DECLARE_HAS_MEMBER_FN(has_ddk_close_protocol_session_multibindable,
DdkCloseProtocolSessionMultibindable);
template <typename D>
constexpr void CheckMultibindable() {
static_assert(has_ddk_open_protocol_session_multibindable<D>::value,
"Multibindable classes must implement DdkOpenProtocolSessionMultibindable");
static_assert(std::is_same<decltype(&D::DdkOpenProtocolSessionMultibindable),
zx_status_t (D::*)(uint32_t, void*)>::value,
"DdkOpenProtocolSessionMultibindable must be a public non-static member function "
"with signature "
"'zx_status_t DdkOpenProtocolSessionMultibindable(uint32_t, void*)'.");
static_assert(has_ddk_close_protocol_session_multibindable<D>::value,
"Multibindable classes must implement DdkCloseProtocolSessionMultibindable");
static_assert(std::is_same<decltype(&D::DdkCloseProtocolSessionMultibindable),
zx_status_t (D::*)(void*)>::value,
"DdkCloseProtocolSessionMultibindable must be a public non-static member function "
"with signature "
"'zx_status_t DdkCloseProtocolSessionMultibindable(void*)'.");
}
// 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 : std::true_type {};
template <typename Base, typename Mixin, typename... Mixins>
struct all_mixins<Base, Mixin, Mixins...>
: std::integral_constant<bool, std::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
#endif // SRC_LIB_DDKTL_INCLUDE_DDKTL_DEVICE_INTERNAL_H_