blob: 6f2713bebb3096eb02c742568e1ad60da2bbc632 [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.
#pragma once
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddktl/device-internal.h>
#include <type_traits>
#include <zircon/assert.h>
// ddk::Device<D, ...>
//
// Notes:
//
// ddk::Device<D, ...> is a mixin class that simplifies writing DDK drivers in
// C++. The DDK's zx_device_t defines a set of function pointer callbacks that
// can be implemented to define standard behavior (e.g., open/close/read/write),
// as well as to implement device lifecycle events (e.g., unbind/release). The
// mixin classes are used to set up the function pointer table to call methods
// from the user's class automatically.
//
// Every ddk::Device subclass must implement the following release callback to
// cleanup resources:
//
// void DdkRelease();
//
//
// :: Available mixins ::
// +----------------------+----------------------------------------------------+
// | Mixin class | Required function implementation |
// +----------------------+----------------------------------------------------+
// | ddk::GetProtocolable | zx_status_t DdkGetProtocol(uint32_t proto_id, |
// | | void* out) |
// | | |
// | ddk::Openable | zx_status_t DdkOpen(zx_device_t** dev_out, |
// | | uint32_t flags) |
// | | |
// | ddk::OpenAtable | zx_status_t DdkOpenAt(zx_device_t** dev_out, |
// | | const char* path, |
// | | uint32_t flags) |
// | | |
// | ddk::Closable | zx_status_t DdkClose(uint32_t flags) |
// | | |
// | ddk::Unbindable | void DdkUnbind() |
// | | |
// | ddk::Readable | zx_status_t DdkRead(void* buf, size_t count, |
// | | zx_off_t off, size_t* actual) |
// | | |
// | ddk::Writable | zx_status_t DdkWrite(const void* buf, |
// | | size_t count, zx_off_t off, |
// | | size_t* actual) |
// | | |
// | ddk::GetSizable | zx_off_t DdkGetSize() |
// | | |
// | ddk::Ioctlable | zx_status_t DdkIoctl(uint32_t op, |
// | | const void* in_buf, |
// | | size_t in_len, void* out_buf, |
// | | size_t out_len, |
// | | size_t* actual) |
// | | |
// | ddk::Messageable | zx_status_t DdkMessage(fidl_msg_t* msg, |
// | | fidl_txn_t* txn) |
// | | |
// | ddk::Suspendable | zx_status_t DdkSuspend(uint32_t flags) |
// | | |
// | ddk::Resumable | zx_status_t DdkResume(uint32_t flags) |
// | | |
// | ddk::Rxrpcable | zx_status_t DdkRxrpc(zx_handle_t channel) |
// +----------------------+----------------------------------------------------+
//
// Note: the ddk::FullDevice type alias may also be used if your device class
// will implement every mixin.
//
//
// :: Example ::
//
// // Define our device type using a type alias.
// class MyDevice;
// using DeviceType = ddk::Device<MyDevice, ddk::Openable, ddk::Closable,
// ddk::Readable, ddk::Unbindable>;
//
// class MyDevice : public DeviceType {
// public:
// MyDevice(zx_device_t* parent)
// : DeviceType(parent) {}
//
// zx_status_t Bind() {
// // Any other setup required by MyDevice. The device_add_args_t will be filled out by the
// // base class.
// return DdkAdd("my-device-name");
// }
//
// // Methods required by the ddk mixins
// zx_status_t DdkOpen(zx_device_t** dev_out, uint32_t flags);
// zx_status_t DdkClose(uint32_t flags);
// zx_status_t DdkRead(void* buf, size_t count, zx_off_t off, size_t* actual);
// void DdkUnbind();
// void DdkRelease();
// };
//
// extern "C" zx_status_t my_bind(zx_device_t* device,
// void** cookie) {
// auto dev = make_unique<MyDevice>(device);
// auto status = dev->Bind();
// if (status == ZX_OK) {
// // devmgr is now in charge of the memory for dev
// dev.release();
// }
// return status;
// }
//
// See also: protocol mixins for setting protocol_id and protocol_ops.
namespace ddk {
struct AnyProtocol {
void* ops;
void* ctx;
};
// base_mixin is a tag that all mixins must inherit from.
using base_mixin = internal::base_mixin;
// base_protocol is a tag used by protocol implementations
using base_protocol = internal::base_protocol;
// DDK Device mixins
template <typename D>
class GetProtocolable : public base_mixin {
protected:
explicit GetProtocolable(zx_protocol_device_t* proto) {
internal::CheckGetProtocolable<D>();
proto->get_protocol = GetProtocol;
}
private:
static zx_status_t GetProtocol(void* ctx, uint32_t proto_id, void* out) {
return static_cast<D*>(ctx)->DdkGetProtocol(proto_id, out);
}
};
template <typename D>
class Openable : public base_mixin {
protected:
explicit Openable(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);
}
};
template <typename D>
class OpenAtable : public base_mixin {
protected:
explicit OpenAtable(zx_protocol_device_t* proto) {
internal::CheckOpenAtable<D>();
proto->open_at = OpenAt;
}
private:
static zx_status_t OpenAt(void* ctx, zx_device_t** dev_out, const char* path,
uint32_t flags) {
return static_cast<D*>(ctx)->DdkOpenAt(dev_out, path, flags);
}
};
template <typename D>
class Closable : public base_mixin {
protected:
explicit Closable(zx_protocol_device_t* proto) {
internal::CheckClosable<D>();
proto->close = Close;
}
private:
static zx_status_t Close(void* ctx, uint32_t flags) {
return static_cast<D*>(ctx)->DdkClose(flags);
}
};
template <typename D>
class Unbindable : public base_mixin {
protected:
explicit Unbindable(zx_protocol_device_t* proto) {
internal::CheckUnbindable<D>();
proto->unbind = Unbind;
}
private:
static void Unbind(void* ctx) {
static_cast<D*>(ctx)->DdkUnbind();
}
};
template <typename D>
class Readable : public base_mixin {
protected:
explicit Readable(zx_protocol_device_t* proto) {
internal::CheckReadable<D>();
proto->read = Read;
}
private:
static zx_status_t Read(void* ctx, void* buf, size_t count, zx_off_t off, size_t* actual) {
return static_cast<D*>(ctx)->DdkRead(buf, count, off, actual);
}
};
template <typename D>
class Writable : public base_mixin {
protected:
explicit Writable(zx_protocol_device_t* proto) {
internal::CheckWritable<D>();
proto->write = Write;
}
private:
static zx_status_t Write(void* ctx, const void* buf, size_t count, zx_off_t off,
size_t* actual) {
return static_cast<D*>(ctx)->DdkWrite(buf, count, off, actual);
}
};
template <typename D>
class GetSizable : public base_mixin {
protected:
explicit GetSizable(zx_protocol_device_t* proto) {
internal::CheckGetSizable<D>();
proto->get_size = GetSize;
}
private:
static zx_off_t GetSize(void* ctx) {
return static_cast<D*>(ctx)->DdkGetSize();
}
};
template <typename D>
class Ioctlable : public base_mixin {
protected:
explicit Ioctlable(zx_protocol_device_t* proto) {
internal::CheckIoctlable<D>();
proto->ioctl = Ioctl;
}
private:
static zx_status_t 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) {
return static_cast<D*>(ctx)->DdkIoctl(op, in_buf, in_len, out_buf, out_len, out_actual);
}
};
template <typename D>
class Messageable : public base_mixin {
protected:
explicit Messageable(zx_protocol_device_t* proto) {
internal::CheckMessageable<D>();
proto->message = Message;
}
private:
static zx_status_t Message(void* ctx, fidl_msg_t* msg, fidl_txn_t* txn) {
return static_cast<D*>(ctx)->DdkMessage(msg, txn);
}
};
template <typename D>
class Suspendable : public base_mixin {
protected:
explicit Suspendable(zx_protocol_device_t* proto) {
internal::CheckSuspendable<D>();
proto->suspend = Suspend;
}
private:
static zx_status_t Suspend(void* ctx, uint32_t flags) {
return static_cast<D*>(ctx)->DdkSuspend(flags);
}
};
template <typename D>
class Resumable : public base_mixin {
protected:
explicit Resumable(zx_protocol_device_t* proto) {
internal::CheckResumable<D>();
proto->resume = Resume;
}
private:
static zx_status_t Resume(void* ctx, uint32_t flags) {
return static_cast<D*>(ctx)->DdkResume(flags);
}
};
template <typename D>
class Rxrpcable : public base_mixin {
protected:
explicit Rxrpcable(zx_protocol_device_t* proto) {
internal::CheckRxrpcable<D>();
proto->rxrpc = Rxrpc;
}
private:
static zx_status_t Rxrpc(void* ctx, zx_handle_t channel) {
return static_cast<D*>(ctx)->DdkRxrpc(channel);
}
};
// Device is templated on the list of mixins that define which DDK device
// methods are implemented. Note that internal::base_device *must* be the
// left-most base class in order to ensure that its constructor runs before the
// mixin constructors. This ensures that ddk_device_proto_ is zero-initialized
// before setting the fields in the mixins.
template <class D, template <typename> class... Mixins>
class Device : public ::ddk::internal::base_device, public Mixins<D>... {
public:
zx_status_t DdkAdd(const char* name, uint32_t flags = 0, zx_device_prop_t* props = nullptr,
uint32_t prop_count = 0, uint32_t proto_id = 0,
const char* proxy_args = nullptr,
zx_handle_t client_remote = ZX_HANDLE_INVALID) {
if (zxdev_ != nullptr) {
return ZX_ERR_BAD_STATE;
}
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = name;
// Since we are stashing this as a D*, we can use ctx in all
// the callback functions and cast it directly to a D*.
args.ctx = static_cast<D*>(this);
args.ops = &ddk_device_proto_;
args.flags = flags;
args.props = props;
args.prop_count = prop_count;
args.proto_id = proto_id;
args.proxy_args = proxy_args;
args.client_remote = client_remote;
AddProtocol(&args);
return device_add(parent_, &args, &zxdev_);
}
void DdkMakeVisible() {
device_make_visible(zxdev());
}
// Removes the device.
// This method may have the side-effect of destroying this object if the
// device's reference count drops to zero.
zx_status_t DdkRemove() {
if (zxdev_ == nullptr) {
return ZX_ERR_BAD_STATE;
}
// The call to |device_remove| must be last since it decrements the
// device's reference count when successful.
zx_device_t* dev = zxdev_;
zxdev_ = nullptr;
return device_remove(dev);
}
zx_status_t DdkGetMetadata(uint32_t type, void* buf, size_t buf_len, size_t* actual) {
return device_get_metadata(zxdev(), type, buf, buf_len, actual);
}
zx_status_t DdkAddMetadata(uint32_t type, const void* data, size_t length) {
return device_add_metadata(zxdev(), type, data, length);
}
zx_status_t DdkPublishMetadata(const char* path, uint32_t type, const void* data,
size_t length) {
return device_publish_metadata(zxdev(), path, type, data, length);
}
const char* name() const { return zxdev() ? device_get_name(zxdev()) : nullptr; }
// The opaque pointer representing this device.
zx_device_t* zxdev() const { return zxdev_; }
// The opaque pointer representing the device's parent.
zx_device_t* parent() const { return parent_; }
void SetState(zx_signals_t stateflag) {
device_state_set(zxdev_, stateflag);
}
void ClearState(zx_signals_t stateflag) {
device_state_clr(zxdev_, stateflag);
}
void ClearAndSetState(zx_signals_t clearflag, zx_signals_t setflag) {
device_state_clr_set(zxdev_, clearflag, setflag);
}
protected:
Device(zx_device_t* parent)
: internal::base_device(parent),
Mixins<D>(&ddk_device_proto_)... {
internal::CheckMixins<Mixins<D>...>();
internal::CheckReleasable<D>();
ddk_device_proto_.release = DdkReleaseThunk;
}
private:
static void DdkReleaseThunk(void* ctx) {
static_cast<D*>(ctx)->DdkRelease();
}
// Add the protocol id and ops if D inherits from a base_protocol implementation.
template <typename T = D>
void AddProtocol(device_add_args_t* args,
typename std::enable_if<internal::is_base_protocol<T>::value, T>::type* dummy = 0) {
auto dev = static_cast<D*>(this);
ZX_ASSERT(dev->ddk_proto_id_ > 0);
args->proto_id = dev->ddk_proto_id_;
args->proto_ops = dev->ddk_proto_ops_;
}
// If D does not inherit from a base_protocol implementation, do nothing.
template <typename T = D>
void AddProtocol(device_add_args_t* args,
typename std::enable_if<!internal::is_base_protocol<T>::value, T>::type* dummy = 0) {}
};
// Convenience type for implementations that would like to override all
// zx_protocol_device_t methods.
template <class D>
using FullDevice = Device<D,
GetProtocolable,
Openable,
OpenAtable,
Closable,
Unbindable,
Readable,
Writable,
GetSizable,
Ioctlable,
Suspendable,
Resumable,
Rxrpcable>;
} // namespace ddk