| // Copyright 2022 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. |
| |
| #include "src/devices/misc/drivers/compat/simple_binding.h" |
| |
| #include <lib/fidl/cpp/wire/internal/transport_channel.h> |
| #include <lib/fidl/cpp/wire/transaction.h> |
| #include <lib/fidl/txn_header.h> |
| #include <lib/zx/channel.h> |
| #include <zircon/syscalls.h> |
| |
| #include <type_traits> |
| |
| namespace devfs { |
| |
| void ChannelTransaction::Dispatch(fidl::IncomingHeaderAndMessage& msg) { |
| binding_->interface_->dispatch_message(std::move(msg), this, nullptr); |
| } |
| |
| zx_status_t ChannelTransaction::Reply(fidl::OutgoingMessage* message, |
| fidl::WriteOptions write_options) { |
| ZX_ASSERT(txid_ != 0); |
| message->set_txid(txid_); |
| txid_ = 0; |
| message->Write(binding_->channel()); |
| return message->status(); |
| } |
| |
| void ChannelTransaction::Close(zx_status_t epitaph) { |
| // We need to make sure binding_ is present, since it may have been released |
| // if Reply() called Close() |
| if (binding_) { |
| fidl_epitaph_write(binding_->channel()->get(), epitaph); |
| binding_.reset(); |
| } |
| } |
| |
| ChannelTransaction::~ChannelTransaction() { |
| if (binding_) { |
| BeginWait(&binding_); |
| } |
| } |
| |
| std::unique_ptr<fidl::Transaction> ChannelTransaction::TakeOwnership() { |
| return std::make_unique<ChannelTransaction>(std::move(*this)); |
| } |
| |
| SimpleBinding::SimpleBinding(async_dispatcher_t* dispatcher, zx::channel channel, |
| fidl::internal::IncomingMessageDispatcher* interface, |
| AnyOnChannelClosedFn on_channel_closed_fn) |
| : async_wait_t({ |
| .state = ASYNC_STATE_INIT, |
| .handler = &MessageHandler, |
| .object = channel.release(), |
| .trigger = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, |
| .options = 0, |
| }), |
| dispatcher_(dispatcher), |
| interface_(interface), |
| on_channel_closed_fn_(std::move(on_channel_closed_fn)) {} |
| |
| SimpleBinding::~SimpleBinding() { |
| zx_handle_close(async_wait_t::object); |
| if (on_channel_closed_fn_) { |
| on_channel_closed_fn_(interface_); |
| } |
| } |
| |
| void SimpleBinding::MessageHandler(async_dispatcher_t* dispatcher, async_wait_t* wait, |
| zx_status_t dispatcher_status, |
| const zx_packet_signal_t* signal) { |
| std::unique_ptr<SimpleBinding> binding(static_cast<SimpleBinding*>(wait)); |
| if (dispatcher_status != ZX_OK) { |
| return; |
| } |
| |
| if (signal->observed & ZX_CHANNEL_READABLE) { |
| uint8_t bytes[ZX_CHANNEL_MAX_MSG_BYTES]; |
| zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| fidl_channel_handle_metadata_t handle_metadata[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| for (uint64_t i = 0; i < signal->count; i++) { |
| fidl::IncomingHeaderAndMessage msg = fidl::MessageRead( |
| zx::unowned_channel(wait->object), fidl::ChannelMessageStorageView{ |
| .bytes = fidl::BufferSpan(bytes, std::size(bytes)), |
| .handles = handles, |
| .handle_metadata = handle_metadata, |
| .handle_capacity = ZX_CHANNEL_MAX_MSG_HANDLES, |
| }); |
| if (!msg.ok()) |
| return; |
| |
| auto* hdr = msg.header(); |
| ChannelTransaction txn(hdr->txid, std::move(binding)); |
| txn.Dispatch(msg); |
| binding = txn.TakeBinding(); |
| if (!binding) { |
| return; |
| } |
| } |
| |
| // Will only get here if every single message was handled synchronously and successfully. |
| BeginWait(&binding); |
| } else { |
| ZX_DEBUG_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED); |
| } |
| } |
| |
| zx_status_t BeginWait(std::unique_ptr<SimpleBinding>* unique_binding) { |
| SimpleBinding* binding = unique_binding->release(); |
| zx_status_t status; |
| if ((status = async_begin_wait(binding->dispatcher_, binding)) != ZX_OK) { |
| // Failed to transfer binding ownership to async dispatcher. |
| unique_binding->reset(binding); |
| } |
| return status; |
| } |
| |
| } // namespace devfs |