| // 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. |
| |
| #pragma once |
| |
| #include <fuchsia/device/llcpp/fidl.h> |
| #include <lib/async/cpp/wait.h> |
| #include <lib/fdio/limits.h> |
| #include <lib/fidl/llcpp/transaction.h> |
| #include <lib/fit/function.h> |
| #include <lib/zx/channel.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <zircon/compiler.h> |
| #include <zircon/fidl.h> |
| #include <zircon/types.h> |
| |
| #include <type_traits> |
| |
| #include <ddktl/fidl.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| |
| #include "async-loop-ref-counted-rpc-handler.h" |
| #include "zircon/assert.h" |
| |
| typedef struct zx_device zx_device_t; |
| |
| namespace devmgr { |
| |
| class Connection; |
| |
| // callback to process a FIDL message. |
| // - |msg| is a decoded FIDL message. |
| // - return value of ERR_DISPATCHER_{INDIRECT,ASYNC} indicates that the reply is |
| // being handled by the callback (forwarded to another server, sent later, |
| // etc, and no reply message should be sent). |
| // - WARNING: Once this callback returns, usage of |msg| is no longer |
| // valid. If a client transmits ERR_DISPATCHER_{INDIRECT,ASYNC}, and intends |
| // to respond asynchronously, they must copy the fields of |msg| they |
| // wish to use at a later point in time. |
| // - otherwise, the return value is treated as the status to send |
| // in the rpc response, and msg.len indicates how much valid data |
| // to send. On error return msg.len will be set to 0. |
| using FidlDispatchFunction = fit::callback<zx_status_t(fidl_msg_t* msg, Connection* txn)>; |
| |
| class DevfsConnection : public fbl::RefCounted<DevfsConnection>, |
| public AsyncLoopRefCountedRpcHandler<DevfsConnection>, |
| public llcpp::fuchsia::device::Controller::Interface { |
| public: |
| DevfsConnection() = default; |
| |
| static void HandleRpc(fbl::RefPtr<DevfsConnection>&& conn, async_dispatcher_t* dispatcher, |
| async::WaitBase* wait, zx_status_t status, |
| const zx_packet_signal_t* signal); |
| |
| fbl::RefPtr<zx_device_t> dev; |
| size_t io_off = 0; |
| uint32_t flags = 0; |
| zx_txid_t last_txid = 0; |
| bool reply_called = false; |
| |
| private: |
| // Attempts to read and dispatch a FIDL message. |
| // |
| // If a message cannot be read, returns an error instead of blocking. |
| zx_status_t ReadMessage(FidlDispatchFunction dispatch); |
| |
| // Synthesizes a FIDL close message. |
| // |
| // This may be invoked when a channel is closed, to simulate dispatching |
| // to the same close function. |
| zx_status_t CloseMessage(FidlDispatchFunction dispatch); |
| |
| void Bind(::fidl::StringView driver, BindCompleter::Sync _completer) override; |
| void Rebind(::fidl::StringView driver, RebindCompleter::Sync _completer) override; |
| void ScheduleUnbind(ScheduleUnbindCompleter::Sync _completer) override; |
| void GetDriverName(GetDriverNameCompleter::Sync _completer) override; |
| void GetDeviceName(GetDeviceNameCompleter::Sync _completer) override; |
| void GetTopologicalPath(GetTopologicalPathCompleter::Sync _completer) override; |
| void GetEventHandle(GetEventHandleCompleter::Sync _completer) override; |
| void GetDriverLogFlags(GetDriverLogFlagsCompleter::Sync _completer) override; |
| void GetDevicePerformanceStates(GetDevicePerformanceStatesCompleter::Sync completer) override; |
| void SetDriverLogFlags(uint32_t clear_flags, uint32_t set_flags, |
| SetDriverLogFlagsCompleter::Sync _completer) override; |
| void DebugSuspend(DebugSuspendCompleter::Sync _completer) override; |
| void DebugResume(DebugResumeCompleter::Sync _completer) override; |
| void RunCompatibilityTests(int64_t hook_wait_time, |
| RunCompatibilityTestsCompleter::Sync _completer) override; |
| void GetDevicePowerCaps(GetDevicePowerCapsCompleter::Sync _completer) override; |
| void SetPerformanceState(uint32_t requested_state, |
| SetPerformanceStateCompleter::Sync _completer) override; |
| void ConfigureAutoSuspend(bool enable, ::llcpp::fuchsia::device::DevicePowerState requested_state, |
| ConfigureAutoSuspendCompleter::Sync _completer) override; |
| |
| void UpdatePowerStateMapping( |
| ::fidl::Array<::llcpp::fuchsia::device::SystemPowerStateInfo, 7> mapping, |
| UpdatePowerStateMappingCompleter::Sync _completer) override; |
| void GetPowerStateMapping(GetPowerStateMappingCompleter::Sync _completer) override; |
| void Suspend(::llcpp::fuchsia::device::DevicePowerState requested_state, |
| SuspendCompleter::Sync _completer) override; |
| void Resume(::llcpp::fuchsia::device::DevicePowerState requested_state, |
| ResumeCompleter::Sync _complete) override; |
| }; |
| |
| class Connection { |
| public: |
| Connection(fidl_txn_t txn, zx_txid_t txid, fbl::RefPtr<DevfsConnection> conn); |
| |
| explicit Connection(const ddk::Connection* conn); |
| |
| Connection(const Connection& other); |
| Connection& operator=(const Connection& other); |
| |
| ~Connection(); |
| |
| fidl_txn_t* Txn() { return &txn_; } |
| |
| zx_txid_t Txid() const { return txid_; } |
| |
| zx::unowned_channel channel() const { |
| ZX_ASSERT_MSG(conn_.get() != nullptr, "Can't get channel =("); |
| return conn_->channel(); |
| } |
| |
| DevfsConnection* devfs_connection() { return conn_.get(); } |
| |
| // Consumes self. |
| ddk::Connection ToDdkConnection(); |
| |
| // Utilizes a |fidl_txn_t| object as a wrapped Connection. |
| // |
| // Only safe to call if |txn| was previously returned by |Connection.Txn()|. |
| static Connection* FromTxn(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(fidl_txn_t* txn); |
| |
| private: |
| fidl_txn_t txn_; |
| zx_txid_t txid_; |
| |
| fbl::RefPtr<DevfsConnection> conn_ = nullptr; |
| }; |
| |
| inline Connection* Connection::FromTxn(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<Connection*>(txn); |
| } |
| |
| inline Connection Connection::CopyTxn(fidl_txn_t* txn) { |
| return *FromTxn(txn); |
| } |
| |
| class Transaction : public fidl::Transaction { |
| public: |
| explicit Transaction(fidl_txn_t* txn) : conn_(Connection::CopyTxn(txn)) {} |
| |
| ~Transaction() { |
| ZX_ASSERT_MSG(status_called_, |
| "Transaction must have it's Status() method used. \ |
| This provides ::DevhostMessage with the correct status value.\n"); |
| } |
| |
| /// Status() return the internal state of the transaction. This MUST be called |
| /// to bridge the Transaction and dispatcher. |
| zx_status_t Status() __WARN_UNUSED_RESULT { |
| status_called_ = true; |
| return status_; |
| } |
| |
| protected: |
| void Reply(fidl::Message msg) final { |
| 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_ = conn_.Txn()->reply(conn_.Txn(), &fidl_msg); |
| msg.ClearHandlesUnsafe(); |
| } |
| |
| void Close(zx_status_t close_status) final { status_ = close_status; } |
| |
| std::unique_ptr<fidl::Transaction> TakeOwnership() final { |
| // |conn_| will keep the channel alive. |
| status_called_ = true; |
| return std::make_unique<Transaction>(*this); |
| } |
| |
| private: |
| devmgr::Connection conn_; |
| zx_status_t status_ = ZX_OK; |
| bool status_called_ = false; |
| }; |
| |
| } // namespace devmgr |