blob: 8211e1412232af9370c2d67f4a3966528cbb61e5 [file] [log] [blame]
// Copyright 2019 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 DDKTL_FIDL_H_
#define DDKTL_FIDL_H_
#include <lib/fidl/llcpp/transaction.h>
#include <lib/zx/channel.h>
#include <zircon/fidl.h>
#include <memory>
#include <type_traits>
namespace ddk {
class Connection {
public:
Connection(fidl_txn_t txn, zx_txid_t txid, uintptr_t devhost_ctx)
: txn_(txn), txid_(txid), devhost_ctx_(devhost_ctx) {}
fidl_txn_t* Txn() { return &txn_; }
const fidl_txn_t* Txn() const { return &txn_; }
zx_txid_t Txid() const { return txid_; }
uintptr_t DevhostContext() const { return devhost_ctx_; }
// Utilizes a |fidl_txn_t| object as a wrapped Connection.
//
// Only safe to call if |txn| was previously returned by |Connection.Txn()|.
static const Connection* FromTxn(const fidl_txn_t* txn);
// Copies txn into a new Connection.
//
// This may be useful for copying a Connection out of stack-allocated scope,
// so a response may be generated asynchronously.
//
// Only safe to call if |txn| was previously returned by |Connection.Txn()|.
static Connection CopyTxn(const fidl_txn_t* txn);
private:
fidl_txn_t txn_;
zx_txid_t txid_;
// Private information only for use by devhost.
uintptr_t devhost_ctx_;
};
inline const Connection* Connection::FromTxn(const fidl_txn_t* txn) {
static_assert(std::is_standard_layout<Connection>::value,
"Cannot cast from non-standard layout class");
static_assert(offsetof(Connection, txn_) == 0, "Connection must be convertable to txn");
return reinterpret_cast<const Connection*>(txn);
}
inline Connection Connection::CopyTxn(const fidl_txn_t* txn) { return *FromTxn(txn); }
} // namespace ddk
// TODO(surajmalhotra): Extend namespace to cover DdkTransaction.
// An implementation of |fidl::Transaction| for using LLCPP bindings in drivers,
// designed to work with ::DdkMessage. If can be used to reply synchronously as in:
// zx_status_t DdkFidlDevice::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
// DdkTransaction transaction(txn);
// fuchsia::hardware::serial::Device::Dispatch(this, msg, &transaction);
// return transaction.Status();
// }
// void DdkFidlDevice::GetClass(GetClassCompleter::Sync completer) {
// completer.Reply(fuchsia::hardware::serial::Class::CONSOLE);
// }
//
// And also can be used to reply asynchronously via ToAsync() call as in:
//
// zx_status_t DdkFidlDevice::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
// DdkTransaction transaction(txn);
// fuchsia::hardware::serial::Device::Dispatch(this, msg, &transaction);
// return ZX_ERR_AYSNC; // Ownership of transaction was taken, can't use transaction.Status()
// here.
// }
// void DdkFidlDevice::GetClass(GetClassCompleter::Sync completer) {
// auto async_completer = new Wrapper(completer.ToAsync());
// DdkScheduleWork([](void* ctx) {
// auto* wrapper = reinterpret_cast<Wrapper*>(ctx);
// wrapper->completer.Reply(fuchsia::hardware::serial::Class::CONSOLE);
// delete wrapper;
// });
// }
//
// Note that this class is not thread safe.
class DdkTransaction : public fidl::Transaction {
public:
explicit DdkTransaction(fidl_txn_t* txn) : connection_(ddk::Connection::CopyTxn(txn)) {}
~DdkTransaction() {
ZX_ASSERT_MSG(ownership_taken_ || status_called_,
"Sync DdkTransaction must have it's Status() method used.\n"
"This provides ::DdkMessage with the correct status value.\n"
"If ToAsync() was called, the DdkTransaction ownership was taken and\n"
"Status() must not be called in ::DdkMessage\n");
}
/// Status() return the internal state of the DDK transaction. This MUST be called
/// to bridge the Transaction and DDK dispatcher.
zx_status_t Status() __WARN_UNUSED_RESULT {
status_called_ = true;
return status_;
}
protected:
void Reply(fidl::Message msg) final {
if (!closed_) {
const fidl_msg_t fidl_msg{
.bytes = msg.bytes().data(),
.handles = msg.handles().data(),
.num_bytes = static_cast<uint32_t>(msg.bytes().size()),
.num_handles = static_cast<uint32_t>(msg.handles().size()),
};
status_ = connection_.Txn()->reply(connection_.Txn(), &fidl_msg);
}
msg.ClearHandlesUnsafe();
}
void Close(zx_status_t epitaph) final {
closed_ = true;
status_ = epitaph;
}
std::unique_ptr<Transaction> TakeOwnership() final {
ownership_taken_ = true;
return std::make_unique<DdkTransaction>(std::move(*this));
}
private:
ddk::Connection connection_; // includes a fidl_txn_t.
zx_status_t status_ = ZX_OK;
bool closed_ = false;
bool status_called_ = false;
bool ownership_taken_ = false;
};
#endif // DDKTL_FIDL_H_