// Copyright 2018 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 <lib/async/wait.h> #include <lib/fidl-async/bind.h> #include <stdlib.h> #include <string.h> #include <zircon/syscalls.h> typedef struct fidl_binding { async_wait_t wait; fidl_dispatch_t* dispatch; async_dispatcher_t* dispatcher; void* ctx; const void* ops; } fidl_binding_t; typedef struct fidl_connection { fidl_txn_t txn; zx_handle_t channel; zx_txid_t txid; fidl_binding_t* binding; } fidl_connection_t; static zx_status_t fidl_reply(fidl_txn_t* txn, const fidl_msg_t* msg) { fidl_connection_t* conn = (fidl_connection_t*)txn; if (conn->txid == 0u) return ZX_ERR_BAD_STATE; if (msg->num_bytes < sizeof(fidl_message_header_t)) return ZX_ERR_INVALID_ARGS; fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes; hdr->txid = conn->txid; conn->txid = 0u; return zx_channel_write(conn->channel, 0, msg->bytes, msg->num_bytes, msg->handles, msg->num_handles); } static void fidl_binding_destroy(fidl_binding_t* binding) { zx_handle_close(binding->wait.object); free(binding); } static void fidl_message_handler(async_dispatcher_t* dispatcher, async_wait_t* wait, zx_status_t status, const zx_packet_signal_t* signal) { fidl_binding_t* binding = (fidl_binding_t*)wait; if (status != ZX_OK) { goto shutdown; } if (signal->observed & ZX_CHANNEL_READABLE) { char bytes[ZX_CHANNEL_MAX_MSG_BYTES]; zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES]; for (uint64_t i = 0; i < signal->count; i++) { fidl_msg_t msg = { .bytes = bytes, .handles = handles, .num_bytes = 0u, .num_handles = 0u, }; status = zx_channel_read(wait->object, 0, bytes, handles, ZX_CHANNEL_MAX_MSG_BYTES, ZX_CHANNEL_MAX_MSG_HANDLES, &msg.num_bytes, &msg.num_handles); if (status == ZX_ERR_SHOULD_WAIT) { break; } if (status != ZX_OK || msg.num_bytes < sizeof(fidl_message_header_t)) { goto shutdown; } fidl_message_header_t* hdr = (fidl_message_header_t*)msg.bytes; fidl_connection_t conn = { .txn.reply = fidl_reply, .channel = wait->object, .txid = hdr->txid, .binding = binding, }; status = binding->dispatch(binding->ctx, &conn.txn, &msg, binding->ops); switch (status) { case ZX_OK: status = async_begin_wait(dispatcher, wait); if (status != ZX_OK) { goto shutdown; } return; case ZX_ERR_ASYNC: return; default: goto shutdown; } } } shutdown: fidl_binding_destroy(binding); } zx_status_t fidl_bind(async_dispatcher_t* dispatcher, zx_handle_t channel, fidl_dispatch_t* dispatch, void* ctx, const void* ops) { fidl_binding_t* binding = calloc(1, sizeof(fidl_binding_t)); binding->wait.handler = fidl_message_handler; binding->wait.object = channel; binding->wait.trigger = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED; binding->dispatch = dispatch; binding->dispatcher = dispatcher; binding->ctx = ctx; binding->ops = ops; zx_status_t status = async_begin_wait(dispatcher, &binding->wait); if (status != ZX_OK) { fidl_binding_destroy(binding); } return status; } typedef struct fidl_async_txn { fidl_connection_t connection; } fidl_async_txn_t; fidl_async_txn_t* fidl_async_txn_create(fidl_txn_t* txn) { fidl_connection_t* connection = (fidl_connection_t*) txn; fidl_async_txn_t* async_txn = calloc(1, sizeof(fidl_async_txn_t)); memcpy(&async_txn->connection, connection, sizeof(*connection)); return async_txn; } fidl_txn_t* fidl_async_txn_borrow(fidl_async_txn_t* async_txn) { return &async_txn->connection.txn; } zx_status_t fidl_async_txn_complete(fidl_async_txn_t* async_txn, bool rebind) { zx_status_t status = ZX_OK; if (rebind) { status = async_begin_wait(async_txn->connection.binding->dispatcher, &async_txn->connection.binding->wait); if (status == ZX_OK) { return ZX_OK; } } fidl_binding_destroy(async_txn->connection.binding); free(async_txn); return status; }