1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <lib/async/wait.h>
6 #include <lib/fidl-async/bind.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <zircon/syscalls.h>
10
11 typedef struct fidl_binding {
12 async_wait_t wait;
13 fidl_dispatch_t* dispatch;
14 async_dispatcher_t* dispatcher;
15 void* ctx;
16 const void* ops;
17 } fidl_binding_t;
18
19 typedef struct fidl_connection {
20 fidl_txn_t txn;
21 zx_handle_t channel;
22 zx_txid_t txid;
23 fidl_binding_t* binding;
24 } fidl_connection_t;
25
fidl_reply(fidl_txn_t * txn,const fidl_msg_t * msg)26 static zx_status_t fidl_reply(fidl_txn_t* txn, const fidl_msg_t* msg) {
27 fidl_connection_t* conn = (fidl_connection_t*)txn;
28 if (conn->txid == 0u)
29 return ZX_ERR_BAD_STATE;
30 if (msg->num_bytes < sizeof(fidl_message_header_t))
31 return ZX_ERR_INVALID_ARGS;
32 fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;
33 hdr->txid = conn->txid;
34 conn->txid = 0u;
35 return zx_channel_write(conn->channel, 0, msg->bytes, msg->num_bytes,
36 msg->handles, msg->num_handles);
37 }
38
fidl_binding_destroy(fidl_binding_t * binding)39 static void fidl_binding_destroy(fidl_binding_t* binding) {
40 zx_handle_close(binding->wait.object);
41 free(binding);
42 }
43
fidl_message_handler(async_dispatcher_t * dispatcher,async_wait_t * wait,zx_status_t status,const zx_packet_signal_t * signal)44 static void fidl_message_handler(async_dispatcher_t* dispatcher,
45 async_wait_t* wait,
46 zx_status_t status,
47 const zx_packet_signal_t* signal) {
48 fidl_binding_t* binding = (fidl_binding_t*)wait;
49 if (status != ZX_OK) {
50 goto shutdown;
51 }
52
53 if (signal->observed & ZX_CHANNEL_READABLE) {
54 char bytes[ZX_CHANNEL_MAX_MSG_BYTES];
55 zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
56 for (uint64_t i = 0; i < signal->count; i++) {
57 fidl_msg_t msg = {
58 .bytes = bytes,
59 .handles = handles,
60 .num_bytes = 0u,
61 .num_handles = 0u,
62 };
63 status = zx_channel_read(wait->object, 0, bytes, handles,
64 ZX_CHANNEL_MAX_MSG_BYTES,
65 ZX_CHANNEL_MAX_MSG_HANDLES,
66 &msg.num_bytes, &msg.num_handles);
67 if (status == ZX_ERR_SHOULD_WAIT) {
68 break;
69 }
70 if (status != ZX_OK || msg.num_bytes < sizeof(fidl_message_header_t)) {
71 goto shutdown;
72 }
73 fidl_message_header_t* hdr = (fidl_message_header_t*)msg.bytes;
74 fidl_connection_t conn = {
75 .txn.reply = fidl_reply,
76 .channel = wait->object,
77 .txid = hdr->txid,
78 .binding = binding,
79 };
80 status = binding->dispatch(binding->ctx, &conn.txn, &msg, binding->ops);
81 switch (status) {
82 case ZX_OK:
83 status = async_begin_wait(dispatcher, wait);
84 if (status != ZX_OK) {
85 goto shutdown;
86 }
87 return;
88 case ZX_ERR_ASYNC:
89 return;
90 default:
91 goto shutdown;
92 }
93 }
94 }
95
96 shutdown:
97 fidl_binding_destroy(binding);
98 }
99
fidl_bind(async_dispatcher_t * dispatcher,zx_handle_t channel,fidl_dispatch_t * dispatch,void * ctx,const void * ops)100 zx_status_t fidl_bind(async_dispatcher_t* dispatcher, zx_handle_t channel,
101 fidl_dispatch_t* dispatch, void* ctx, const void* ops) {
102 fidl_binding_t* binding = calloc(1, sizeof(fidl_binding_t));
103 binding->wait.handler = fidl_message_handler;
104 binding->wait.object = channel;
105 binding->wait.trigger = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
106 binding->dispatch = dispatch;
107 binding->dispatcher = dispatcher;
108 binding->ctx = ctx;
109 binding->ops = ops;
110 zx_status_t status = async_begin_wait(dispatcher, &binding->wait);
111 if (status != ZX_OK) {
112 fidl_binding_destroy(binding);
113 }
114 return status;
115 }
116
117 typedef struct fidl_async_txn {
118 fidl_connection_t connection;
119 } fidl_async_txn_t;
120
fidl_async_txn_create(fidl_txn_t * txn)121 fidl_async_txn_t* fidl_async_txn_create(fidl_txn_t* txn) {
122 fidl_connection_t* connection = (fidl_connection_t*) txn;
123
124 fidl_async_txn_t* async_txn = calloc(1, sizeof(fidl_async_txn_t));
125 memcpy(&async_txn->connection, connection, sizeof(*connection));
126
127 return async_txn;
128 }
129
fidl_async_txn_borrow(fidl_async_txn_t * async_txn)130 fidl_txn_t* fidl_async_txn_borrow(fidl_async_txn_t* async_txn) {
131 return &async_txn->connection.txn;
132 }
133
fidl_async_txn_complete(fidl_async_txn_t * async_txn,bool rebind)134 zx_status_t fidl_async_txn_complete(fidl_async_txn_t* async_txn, bool rebind) {
135 zx_status_t status = ZX_OK;
136 if (rebind) {
137 status = async_begin_wait(async_txn->connection.binding->dispatcher,
138 &async_txn->connection.binding->wait);
139 if (status == ZX_OK) {
140 return ZX_OK;
141 }
142 }
143
144 fidl_binding_destroy(async_txn->connection.binding);
145 free(async_txn);
146 return status;
147 }
148