source: mainline/uspace/lib/c/generic/async/server.c@ 95faa4d

Last change on this file since 95faa4d was c8afd5a, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Have async_poke() check whether the poke is necessary.

This was originally done in fibril_synch.c and the previous commit accidentally
removed it.

  • Property mode set to 100644
File size: 45.1 KB
RevLine 
[06502f7d]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
[06502f7d]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[b2951e2]27 */
28
[a46da63]29/** @addtogroup libc
[b2951e2]30 * @{
31 */
32/** @file
[c07544d3]33 */
[06502f7d]34
[80649a91]35/**
36 * Asynchronous library
37 *
[c07544d3]38 * The aim of this library is to provide a facility for writing programs which
39 * utilize the asynchronous nature of HelenOS IPC, yet using a normal way of
40 * programming.
[80649a91]41 *
[79ae36dd]42 * You should be able to write very simple multithreaded programs. The async
43 * framework will automatically take care of most of the synchronization
44 * problems.
[80649a91]45 *
[9591265]46 * Example of use (pseudo C):
[c07544d3]47 *
[80649a91]48 * 1) Multithreaded client application
[9591265]49 *
[c07544d3]50 * fibril_create(fibril1, ...);
51 * fibril_create(fibril2, ...);
52 * ...
53 *
54 * int fibril1(void *arg)
55 * {
[79ae36dd]56 * conn = async_connect_me_to(...);
57 *
58 * exch = async_exchange_begin(conn);
59 * c1 = async_send(exch);
60 * async_exchange_end(exch);
61 *
62 * exch = async_exchange_begin(conn);
63 * c2 = async_send(exch);
64 * async_exchange_end(exch);
65 *
[c07544d3]66 * async_wait_for(c1);
67 * async_wait_for(c2);
68 * ...
69 * }
[80649a91]70 *
71 *
72 * 2) Multithreaded server application
73 *
[c07544d3]74 * main()
75 * {
76 * async_manager();
77 * }
78 *
[01c3bb4]79 * port_handler(ichandle, *icall)
[c07544d3]80 * {
81 * if (want_refuse) {
[01c3bb4]82 * async_answer_0(ichandle, ELIMIT);
[c07544d3]83 * return;
84 * }
[01c3bb4]85 * async_answer_0(ichandle, EOK);
[80649a91]86 *
[01c3bb4]87 * chandle = async_get_call(&call);
88 * somehow_handle_the_call(chandle, call);
89 * async_answer_2(chandle, 1, 2, 3);
[53ca318]90 *
[01c3bb4]91 * chandle = async_get_call(&call);
[c07544d3]92 * ...
93 * }
[a2cd194]94 *
[80649a91]95 */
[9591265]96
[64d2b10]97#define LIBC_ASYNC_C_
98#include <ipc/ipc.h>
[80649a91]99#include <async.h>
[49a796f1]100#include "../private/async.h"
[64d2b10]101#undef LIBC_ASYNC_C_
102
[8820544]103#include <ipc/irq.h>
104#include <ipc/event.h>
[64d2b10]105#include <futex.h>
[bc1f1c2]106#include <fibril.h>
[d9c8c81]107#include <adt/hash_table.h>
[853802e]108#include <adt/hash.h>
[d9c8c81]109#include <adt/list.h>
[80649a91]110#include <assert.h>
111#include <errno.h>
[daa90e8]112#include <sys/time.h>
[c0699467]113#include <libarch/barrier.h>
[3e6a98c5]114#include <stdbool.h>
[38d150e]115#include <stdlib.h>
[79ae36dd]116#include <mem.h>
117#include <stdlib.h>
[e2ab36f1]118#include <macros.h>
[101516d]119#include <as.h>
[ae6021d]120#include <abi/mm/as.h>
[49a796f1]121#include "../private/libc.h"
[5da7199]122
[79ae36dd]123/** Async framework global futex */
[927a181e]124futex_t async_futex = FUTEX_INITIALIZER;
[80649a91]125
[8619f25]126/** Number of threads waiting for IPC in the kernel. */
[c8afd5a]127static atomic_t threads_in_ipc_wait = { 0 };
[8619f25]128
[79ae36dd]129/** Call data */
[80649a91]130typedef struct {
131 link_t link;
[a35b458]132
[eadaeae8]133 cap_call_handle_t chandle;
[80649a91]134 ipc_call_t call;
135} msg_t;
136
[79ae36dd]137/* Client connection data */
[c80fdd0]138typedef struct {
[062d900]139 ht_link_t link;
[a35b458]140
[649f087]141 task_id_t in_task_id;
[79ae36dd]142 atomic_t refcnt;
[c80fdd0]143 void *data;
144} client_t;
145
[79ae36dd]146/* Server connection data */
[80649a91]147typedef struct {
[49d072e]148 awaiter_t wdata;
[a35b458]149
[e70bfa5]150 /** Hash table link. */
[062d900]151 ht_link_t link;
[a35b458]152
[e2ab36f1]153 /** Incoming client task ID. */
154 task_id_t in_task_id;
[a35b458]155
[e70bfa5]156 /** Incoming phone hash. */
[96b02eb9]157 sysarg_t in_phone_hash;
[a35b458]158
[23882034]159 /** Link to the client tracking structure. */
160 client_t *client;
[a35b458]161
[e70bfa5]162 /** Messages that should be delivered to this fibril. */
[b72efe8]163 list_t msg_queue;
[a35b458]164
[e70bfa5]165 /** Identification of the opening call. */
[eadaeae8]166 cap_call_handle_t chandle;
[a35b458]167
[e70bfa5]168 /** Call data of the opening call. */
[80649a91]169 ipc_call_t call;
[a35b458]170
[e70bfa5]171 /** Identification of the closing call. */
[eadaeae8]172 cap_call_handle_t close_chandle;
[a35b458]173
[e70bfa5]174 /** Fibril function that will be used to handle the connection. */
[b688fd8]175 async_port_handler_t handler;
[a35b458]176
[b688fd8]177 /** Client data */
178 void *data;
[80649a91]179} connection_t;
180
[8820544]181/* Notification data */
182typedef struct {
183 ht_link_t link;
[a35b458]184
[8820544]185 /** Notification method */
186 sysarg_t imethod;
[a35b458]187
[8820544]188 /** Notification handler */
189 async_notification_handler_t handler;
[a35b458]190
[8820544]191 /** Notification data */
192 void *data;
193} notification_t;
194
[bc1f1c2]195/** Identifier of the incoming connection handled by the current fibril. */
[79ae36dd]196static fibril_local connection_t *fibril_connection;
[e70bfa5]197
[46eec3b]198static void *default_client_data_constructor(void)
199{
200 return NULL;
201}
202
203static void default_client_data_destructor(void *data)
204{
205}
206
207static async_client_data_ctor_t async_client_data_create =
208 default_client_data_constructor;
209static async_client_data_dtor_t async_client_data_destroy =
210 default_client_data_destructor;
211
212void async_set_client_data_constructor(async_client_data_ctor_t ctor)
213{
[f302586]214 assert(async_client_data_create == default_client_data_constructor);
[46eec3b]215 async_client_data_create = ctor;
216}
217
218void async_set_client_data_destructor(async_client_data_dtor_t dtor)
219{
[f302586]220 assert(async_client_data_destroy == default_client_data_destructor);
[46eec3b]221 async_client_data_destroy = dtor;
222}
223
[c80fdd0]224static hash_table_t client_hash_table;
[c07544d3]225static hash_table_t conn_hash_table;
[8820544]226static hash_table_t notification_hash_table;
[c07544d3]227static LIST_INITIALIZE(timeout_list);
228
[8820544]229static sysarg_t notification_avail = 0;
230
231static size_t client_key_hash(void *key)
[c80fdd0]232{
[8820544]233 task_id_t in_task_id = *(task_id_t *) key;
234 return in_task_id;
[c80fdd0]235}
236
[062d900]237static size_t client_hash(const ht_link_t *item)
[c80fdd0]238{
[062d900]239 client_t *client = hash_table_get_inst(item, client_t, link);
240 return client_key_hash(&client->in_task_id);
[c80fdd0]241}
242
[8820544]243static bool client_key_equal(void *key, const ht_link_t *item)
[c80fdd0]244{
[8820544]245 task_id_t in_task_id = *(task_id_t *) key;
[062d900]246 client_t *client = hash_table_get_inst(item, client_t, link);
[8820544]247 return in_task_id == client->in_task_id;
[c80fdd0]248}
249
250/** Operations for the client hash table. */
[062d900]251static hash_table_ops_t client_hash_table_ops = {
[c80fdd0]252 .hash = client_hash,
[062d900]253 .key_hash = client_key_hash,
254 .key_equal = client_key_equal,
[4e00f87]255 .equal = NULL,
256 .remove_callback = NULL
[c80fdd0]257};
[80649a91]258
[853802e]259typedef struct {
260 task_id_t task_id;
261 sysarg_t phone_hash;
262} conn_key_t;
263
264/** Compute hash into the connection hash table
[e70bfa5]265 *
[853802e]266 * The hash is based on the source task ID and the source phone hash. The task
267 * ID is included in the hash because a phone hash alone might not be unique
268 * while we still track connections for killed tasks due to kernel's recycling
269 * of phone structures.
270 *
271 * @param key Pointer to the connection key structure.
[c07544d3]272 *
273 * @return Index into the connection hash table.
[e70bfa5]274 *
275 */
[062d900]276static size_t conn_key_hash(void *key)
[450cd3a]277{
[853802e]278 conn_key_t *ck = (conn_key_t *) key;
279
280 size_t hash = 0;
281 hash = hash_combine(hash, LOWER32(ck->task_id));
282 hash = hash_combine(hash, UPPER32(ck->task_id));
283 hash = hash_combine(hash, ck->phone_hash);
284 return hash;
[450cd3a]285}
[06502f7d]286
[062d900]287static size_t conn_hash(const ht_link_t *item)
[450cd3a]288{
[062d900]289 connection_t *conn = hash_table_get_inst(item, connection_t, link);
[853802e]290 return conn_key_hash(&(conn_key_t){
291 .task_id = conn->in_task_id,
292 .phone_hash = conn->in_phone_hash
293 });
[450cd3a]294}
[06502f7d]295
[062d900]296static bool conn_key_equal(void *key, const ht_link_t *item)
[450cd3a]297{
[853802e]298 conn_key_t *ck = (conn_key_t *) key;
[062d900]299 connection_t *conn = hash_table_get_inst(item, connection_t, link);
[853802e]300 return ((ck->task_id == conn->in_task_id) &&
301 (ck->phone_hash == conn->in_phone_hash));
[450cd3a]302}
303
[e70bfa5]304/** Operations for the connection hash table. */
[062d900]305static hash_table_ops_t conn_hash_table_ops = {
[80649a91]306 .hash = conn_hash,
[062d900]307 .key_hash = conn_key_hash,
308 .key_equal = conn_key_equal,
[4e00f87]309 .equal = NULL,
310 .remove_callback = NULL
[80649a91]311};
312
[9ef495f]313static client_t *async_client_get(task_id_t client_id, bool create)
314{
315 client_t *client = NULL;
[a35b458]316
[9ef495f]317 futex_down(&async_futex);
318 ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
319 if (link) {
320 client = hash_table_get_inst(link, client_t, link);
321 atomic_inc(&client->refcnt);
322 } else if (create) {
323 client = malloc(sizeof(client_t));
324 if (client) {
325 client->in_task_id = client_id;
326 client->data = async_client_data_create();
[a35b458]327
[9ef495f]328 atomic_set(&client->refcnt, 1);
329 hash_table_insert(&client_hash_table, &client->link);
330 }
331 }
[a35b458]332
[9ef495f]333 futex_up(&async_futex);
334 return client;
335}
336
337static void async_client_put(client_t *client)
338{
339 bool destroy;
[a35b458]340
[9ef495f]341 futex_down(&async_futex);
[a35b458]342
[9ef495f]343 if (atomic_predec(&client->refcnt) == 0) {
344 hash_table_remove(&client_hash_table, &client->in_task_id);
345 destroy = true;
346 } else
347 destroy = false;
[a35b458]348
[9ef495f]349 futex_up(&async_futex);
[a35b458]350
[9ef495f]351 if (destroy) {
352 if (client->data)
353 async_client_data_destroy(client->data);
[a35b458]354
[9ef495f]355 free(client);
356 }
357}
358
359/** Wrapper for client connection fibril.
360 *
361 * When a new connection arrives, a fibril with this implementing
362 * function is created.
363 *
364 * @param arg Connection structure pointer.
365 *
366 * @return Always zero.
367 *
368 */
[b7fd2a0]369static errno_t connection_fibril(void *arg)
[9ef495f]370{
371 assert(arg);
[a35b458]372
[9ef495f]373 /*
374 * Setup fibril-local connection pointer.
375 */
376 fibril_connection = (connection_t *) arg;
[a35b458]377
[9ef495f]378 /*
379 * Add our reference for the current connection in the client task
380 * tracking structure. If this is the first reference, create and
381 * hash in a new tracking structure.
382 */
[a35b458]383
[9ef495f]384 client_t *client = async_client_get(fibril_connection->in_task_id, true);
385 if (!client) {
[01c3bb4]386 ipc_answer_0(fibril_connection->chandle, ENOMEM);
[9ef495f]387 return 0;
388 }
[a35b458]389
[9ef495f]390 fibril_connection->client = client;
[a35b458]391
[9ef495f]392 /*
393 * Call the connection handler function.
394 */
[01c3bb4]395 fibril_connection->handler(fibril_connection->chandle,
[9ef495f]396 &fibril_connection->call, fibril_connection->data);
[a35b458]397
[9ef495f]398 /*
399 * Remove the reference for this client task connection.
400 */
401 async_client_put(client);
[a35b458]402
[9ef495f]403 /*
404 * Remove myself from the connection hash table.
405 */
406 futex_down(&async_futex);
[853802e]407 hash_table_remove(&conn_hash_table, &(conn_key_t){
408 .task_id = fibril_connection->in_task_id,
409 .phone_hash = fibril_connection->in_phone_hash
410 });
[9ef495f]411 futex_up(&async_futex);
[a35b458]412
[9ef495f]413 /*
414 * Answer all remaining messages with EHANGUP.
415 */
416 while (!list_empty(&fibril_connection->msg_queue)) {
417 msg_t *msg =
418 list_get_instance(list_first(&fibril_connection->msg_queue),
419 msg_t, link);
[a35b458]420
[9ef495f]421 list_remove(&msg->link);
[01c3bb4]422 ipc_answer_0(msg->chandle, EHANGUP);
[9ef495f]423 free(msg);
424 }
[a35b458]425
[9ef495f]426 /*
427 * If the connection was hung-up, answer the last call,
428 * i.e. IPC_M_PHONE_HUNGUP.
429 */
[01c3bb4]430 if (fibril_connection->close_chandle)
431 ipc_answer_0(fibril_connection->close_chandle, EOK);
[a35b458]432
[9ef495f]433 free(fibril_connection);
[d5c1051]434 return EOK;
[9ef495f]435}
436
437/** Create a new fibril for a new connection.
438 *
[01c3bb4]439 * Create new fibril for connection, fill in connection structures and insert it
440 * into the hash table, so that later we can easily do routing of messages to
441 * particular fibrils.
[9ef495f]442 *
[01c3bb4]443 * @param in_task_id Identification of the incoming connection.
444 * @param in_phone_hash Identification of the incoming connection.
445 * @param chandle Handle of the opening IPC_M_CONNECT_ME_TO call.
446 * If chandle is CAP_NIL, the connection was opened by
447 * accepting the IPC_M_CONNECT_TO_ME call and this
448 * function is called directly by the server.
449 * @param call Call data of the opening call.
450 * @param handler Connection handler.
451 * @param data Client argument to pass to the connection handler.
[9ef495f]452 *
[01c3bb4]453 * @return New fibril id or NULL on failure.
[9ef495f]454 *
455 */
456static fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
[eadaeae8]457 cap_call_handle_t chandle, ipc_call_t *call, async_port_handler_t handler,
[9ef495f]458 void *data)
459{
460 connection_t *conn = malloc(sizeof(*conn));
461 if (!conn) {
[01c3bb4]462 if (chandle != CAP_NIL)
463 ipc_answer_0(chandle, ENOMEM);
[a35b458]464
[9ef495f]465 return (uintptr_t) NULL;
466 }
[a35b458]467
[9ef495f]468 conn->in_task_id = in_task_id;
469 conn->in_phone_hash = in_phone_hash;
470 list_initialize(&conn->msg_queue);
[01c3bb4]471 conn->chandle = chandle;
472 conn->close_chandle = CAP_NIL;
[9ef495f]473 conn->handler = handler;
474 conn->data = data;
[a35b458]475
[9ef495f]476 if (call)
477 conn->call = *call;
[a35b458]478
[9ef495f]479 /* We will activate the fibril ASAP */
480 conn->wdata.active = true;
481 conn->wdata.fid = fibril_create(connection_fibril, conn);
[a35b458]482
[9ef495f]483 if (conn->wdata.fid == 0) {
484 free(conn);
[a35b458]485
[01c3bb4]486 if (chandle != CAP_NIL)
487 ipc_answer_0(chandle, ENOMEM);
[a35b458]488
[9ef495f]489 return (uintptr_t) NULL;
490 }
[a35b458]491
[9ef495f]492 /* Add connection to the connection hash table */
[a35b458]493
[9ef495f]494 futex_down(&async_futex);
495 hash_table_insert(&conn_hash_table, &conn->link);
496 futex_up(&async_futex);
[a35b458]497
[9ef495f]498 fibril_add_ready(conn->wdata.fid);
[a35b458]499
[9ef495f]500 return conn->wdata.fid;
501}
502
[78bb04b]503/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
504 *
505 * Ask through phone for a new connection to some service.
506 *
507 * @param exch Exchange for sending the message.
508 * @param iface Callback interface.
509 * @param arg1 User defined argument.
510 * @param arg2 User defined argument.
511 * @param handler Callback handler.
512 * @param data Handler data.
513 * @param port_id ID of the newly created port.
514 *
[cde999a]515 * @return Zero on success or an error code.
[78bb04b]516 *
517 */
[b7fd2a0]518errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
[78bb04b]519 sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
520{
521 if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
522 return EINVAL;
[a35b458]523
[78bb04b]524 if (exch == NULL)
525 return ENOENT;
[a35b458]526
[78bb04b]527 ipc_call_t answer;
528 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
529 &answer);
[a35b458]530
[fda19b8]531 errno_t rc;
532 async_wait_for(req, &rc);
533 if (rc != EOK)
534 return rc;
[a35b458]535
[fda19b8]536 rc = async_create_port_internal(iface, handler, data, port_id);
537 if (rc != EOK)
538 return rc;
[a35b458]539
[fda19b8]540 sysarg_t phone_hash = IPC_GET_ARG5(answer);
[78bb04b]541 fid_t fid = async_new_connection(answer.in_task_id, phone_hash,
[01c3bb4]542 CAP_NIL, NULL, handler, data);
[78bb04b]543 if (fid == (uintptr_t) NULL)
544 return ENOMEM;
[a35b458]545
[78bb04b]546 return EOK;
547}
548
[8820544]549static size_t notification_key_hash(void *key)
550{
551 sysarg_t id = *(sysarg_t *) key;
552 return id;
553}
554
555static size_t notification_hash(const ht_link_t *item)
556{
557 notification_t *notification =
558 hash_table_get_inst(item, notification_t, link);
559 return notification_key_hash(&notification->imethod);
560}
561
562static bool notification_key_equal(void *key, const ht_link_t *item)
563{
564 sysarg_t id = *(sysarg_t *) key;
565 notification_t *notification =
566 hash_table_get_inst(item, notification_t, link);
567 return id == notification->imethod;
568}
569
570/** Operations for the notification hash table. */
571static hash_table_ops_t notification_hash_table_ops = {
572 .hash = notification_hash,
573 .key_hash = notification_key_hash,
574 .key_equal = notification_key_equal,
575 .equal = NULL,
576 .remove_callback = NULL
577};
578
[e70bfa5]579/** Sort in current fibril's timeout request.
[49d072e]580 *
[c07544d3]581 * @param wd Wait data of the current fibril.
582 *
[49d072e]583 */
[b6ee5b1]584void async_insert_timeout(awaiter_t *wd)
[49d072e]585{
[79ae36dd]586 assert(wd);
[a35b458]587
[f53cc81]588 wd->to_event.occurred = false;
589 wd->to_event.inlist = true;
[a35b458]590
[b72efe8]591 link_t *tmp = timeout_list.head.next;
592 while (tmp != &timeout_list.head) {
[3bacee1]593 awaiter_t *cur =
594 list_get_instance(tmp, awaiter_t, to_event.link);
[a35b458]595
[f53cc81]596 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
[49d072e]597 break;
[a35b458]598
[49d072e]599 tmp = tmp->next;
600 }
[a35b458]601
[b72efe8]602 list_insert_before(&wd->to_event.link, tmp);
[49d072e]603}
604
[e70bfa5]605/** Try to route a call to an appropriate connection fibril.
[80649a91]606 *
[36c9234]607 * If the proper connection fibril is found, a message with the call is added to
608 * its message queue. If the fibril was not active, it is activated and all
609 * timeouts are unregistered.
610 *
[01c3bb4]611 * @param chandle Handle of the incoming call.
612 * @param call Data of the incoming call.
[c07544d3]613 *
614 * @return False if the call doesn't match any connection.
[47b7006]615 * @return True if the call was passed to the respective connection fibril.
[36c9234]616 *
[80649a91]617 */
[eadaeae8]618static bool route_call(cap_call_handle_t chandle, ipc_call_t *call)
[450cd3a]619{
[79ae36dd]620 assert(call);
[a35b458]621
[01ff41c]622 futex_down(&async_futex);
[a35b458]623
[853802e]624 ht_link_t *link = hash_table_find(&conn_hash_table, &(conn_key_t){
625 .task_id = call->in_task_id,
626 .phone_hash = call->in_phone_hash
627 });
[8820544]628 if (!link) {
[01ff41c]629 futex_up(&async_futex);
[c07544d3]630 return false;
[450cd3a]631 }
[a35b458]632
[8820544]633 connection_t *conn = hash_table_get_inst(link, connection_t, link);
[a35b458]634
[c07544d3]635 msg_t *msg = malloc(sizeof(*msg));
636 if (!msg) {
637 futex_up(&async_futex);
638 return false;
639 }
[a35b458]640
[01c3bb4]641 msg->chandle = chandle;
[80649a91]642 msg->call = *call;
643 list_append(&msg->link, &conn->msg_queue);
[a35b458]644
[228e490]645 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
[01c3bb4]646 conn->close_chandle = chandle;
[a35b458]647
[36c9234]648 /* If the connection fibril is waiting for an event, activate it */
[49d072e]649 if (!conn->wdata.active) {
[a35b458]650
[49d072e]651 /* If in timeout list, remove it */
[f53cc81]652 if (conn->wdata.to_event.inlist) {
653 conn->wdata.to_event.inlist = false;
654 list_remove(&conn->wdata.to_event.link);
[49d072e]655 }
[a35b458]656
[c07544d3]657 conn->wdata.active = true;
[bc1f1c2]658 fibril_add_ready(conn->wdata.fid);
[80649a91]659 }
[a35b458]660
[01ff41c]661 futex_up(&async_futex);
[c07544d3]662 return true;
663}
[80649a91]664
[c170438]665/** Process notification.
[c07544d3]666 *
[c170438]667 * @param call Data of the incoming call.
[58563585]668 *
[c07544d3]669 */
[01c3bb4]670static void process_notification(ipc_call_t *call)
[c07544d3]671{
[8820544]672 async_notification_handler_t handler = NULL;
673 void *data = NULL;
[c170438]674
675 assert(call);
[a35b458]676
[8820544]677 futex_down(&async_futex);
[a35b458]678
[8820544]679 ht_link_t *link = hash_table_find(&notification_hash_table,
[c170438]680 &IPC_GET_IMETHOD(*call));
[8820544]681 if (link) {
682 notification_t *notification =
683 hash_table_get_inst(link, notification_t, link);
684 handler = notification->handler;
685 data = notification->data;
686 }
[a35b458]687
[8820544]688 futex_up(&async_futex);
[a35b458]689
[8820544]690 if (handler)
[01c3bb4]691 handler(call, data);
[80649a91]692}
693
[8820544]694/** Subscribe to IRQ notification.
695 *
696 * @param inr IRQ number.
697 * @param handler Notification handler.
698 * @param data Notification handler client data.
699 * @param ucode Top-half pseudocode handler.
700 *
[071a1ddb]701 * @param[out] handle IRQ capability handle on success.
702 *
[cde999a]703 * @return An error code.
[8820544]704 *
705 */
[b7fd2a0]706errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
[eadaeae8]707 void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
[8820544]708{
709 notification_t *notification =
710 (notification_t *) malloc(sizeof(notification_t));
711 if (!notification)
712 return ENOMEM;
[a35b458]713
[8820544]714 futex_down(&async_futex);
[a35b458]715
[8820544]716 sysarg_t imethod = notification_avail;
717 notification_avail++;
[a35b458]718
[8820544]719 notification->imethod = imethod;
720 notification->handler = handler;
721 notification->data = data;
[a35b458]722
[8820544]723 hash_table_insert(&notification_hash_table, &notification->link);
[a35b458]724
[8820544]725 futex_up(&async_futex);
[a35b458]726
[eadaeae8]727 cap_irq_handle_t ihandle;
728 errno_t rc = ipc_irq_subscribe(inr, imethod, ucode, &ihandle);
[071a1ddb]729 if (rc == EOK && handle != NULL) {
[eadaeae8]730 *handle = ihandle;
[9233e9d]731 }
[071a1ddb]732 return rc;
[8820544]733}
734
735/** Unsubscribe from IRQ notification.
736 *
[eadaeae8]737 * @param handle IRQ capability handle.
[8820544]738 *
[cde999a]739 * @return Zero on success or an error code.
[8820544]740 *
741 */
[eadaeae8]742errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
[8820544]743{
744 // TODO: Remove entry from hash table
745 // to avoid memory leak
[a35b458]746
[eadaeae8]747 return ipc_irq_unsubscribe(ihandle);
[8820544]748}
749
750/** Subscribe to event notifications.
751 *
752 * @param evno Event type to subscribe.
753 * @param handler Notification handler.
754 * @param data Notification handler client data.
755 *
[cde999a]756 * @return Zero on success or an error code.
[8820544]757 *
758 */
[b7fd2a0]759errno_t async_event_subscribe(event_type_t evno,
[8820544]760 async_notification_handler_t handler, void *data)
761{
762 notification_t *notification =
763 (notification_t *) malloc(sizeof(notification_t));
764 if (!notification)
765 return ENOMEM;
[a35b458]766
[8820544]767 futex_down(&async_futex);
[a35b458]768
[8820544]769 sysarg_t imethod = notification_avail;
770 notification_avail++;
[a35b458]771
[8820544]772 notification->imethod = imethod;
773 notification->handler = handler;
774 notification->data = data;
[a35b458]775
[8820544]776 hash_table_insert(&notification_hash_table, &notification->link);
[a35b458]777
[8820544]778 futex_up(&async_futex);
[a35b458]779
[8820544]780 return ipc_event_subscribe(evno, imethod);
781}
782
783/** Subscribe to task event notifications.
784 *
785 * @param evno Event type to subscribe.
786 * @param handler Notification handler.
787 * @param data Notification handler client data.
788 *
[cde999a]789 * @return Zero on success or an error code.
[8820544]790 *
791 */
[b7fd2a0]792errno_t async_event_task_subscribe(event_task_type_t evno,
[8820544]793 async_notification_handler_t handler, void *data)
794{
795 notification_t *notification =
796 (notification_t *) malloc(sizeof(notification_t));
797 if (!notification)
798 return ENOMEM;
[a35b458]799
[8820544]800 futex_down(&async_futex);
[a35b458]801
[8820544]802 sysarg_t imethod = notification_avail;
803 notification_avail++;
[a35b458]804
[8820544]805 notification->imethod = imethod;
806 notification->handler = handler;
807 notification->data = data;
[a35b458]808
[8820544]809 hash_table_insert(&notification_hash_table, &notification->link);
[a35b458]810
[8820544]811 futex_up(&async_futex);
[a35b458]812
[8820544]813 return ipc_event_task_subscribe(evno, imethod);
814}
815
816/** Unmask event notifications.
817 *
818 * @param evno Event type to unmask.
819 *
820 * @return Value returned by the kernel.
821 *
822 */
[b7fd2a0]823errno_t async_event_unmask(event_type_t evno)
[8820544]824{
825 return ipc_event_unmask(evno);
826}
827
828/** Unmask task event notifications.
829 *
830 * @param evno Event type to unmask.
831 *
832 * @return Value returned by the kernel.
833 *
834 */
[b7fd2a0]835errno_t async_event_task_unmask(event_task_type_t evno)
[8820544]836{
837 return ipc_event_task_unmask(evno);
838}
839
[e70bfa5]840/** Return new incoming message for the current (fibril-local) connection.
841 *
[01c3bb4]842 * @param call Storage where the incoming call data will be stored.
843 * @param usecs Timeout in microseconds. Zero denotes no timeout.
[e70bfa5]844 *
[01c3bb4]845 * @return If no timeout was specified, then a handle of the incoming call is
846 * returned. If a timeout is specified, then a handle of the incoming
847 * call is returned unless the timeout expires prior to receiving a
848 * message. In that case zero CAP_NIL is returned.
[e70bfa5]849 */
[eadaeae8]850cap_call_handle_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
[80649a91]851{
[79ae36dd]852 assert(call);
853 assert(fibril_connection);
[a35b458]854
[7c3fb9b]855 /*
856 * Why doing this?
[79ae36dd]857 * GCC 4.1.0 coughs on fibril_connection-> dereference.
[6c46350]858 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
[c07544d3]859 * I would never expect to find so many errors in
860 * a compiler.
[6c46350]861 */
[79ae36dd]862 connection_t *conn = fibril_connection;
[a35b458]863
[01ff41c]864 futex_down(&async_futex);
[a35b458]865
[49d072e]866 if (usecs) {
[45cbcaf4]867 getuptime(&conn->wdata.to_event.expires);
[7f9d97f3]868 tv_add_diff(&conn->wdata.to_event.expires, usecs);
[c07544d3]869 } else
[f53cc81]870 conn->wdata.to_event.inlist = false;
[a35b458]871
[e70bfa5]872 /* If nothing in queue, wait until something arrives */
[6c46350]873 while (list_empty(&conn->msg_queue)) {
[01c3bb4]874 if (conn->close_chandle) {
[8c8f8d6]875 /*
876 * Handle the case when the connection was already
877 * closed by the client but the server did not notice
878 * the first IPC_M_PHONE_HUNGUP call and continues to
879 * call async_get_call_timeout(). Repeat
[47b7006]880 * IPC_M_PHONE_HUNGUP until the caller notices.
[8c8f8d6]881 */
882 memset(call, 0, sizeof(ipc_call_t));
[228e490]883 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
[8c8f8d6]884 futex_up(&async_futex);
[01c3bb4]885 return conn->close_chandle;
[8c8f8d6]886 }
[a35b458]887
[085bd54]888 if (usecs)
[b6ee5b1]889 async_insert_timeout(&conn->wdata);
[a35b458]890
[c07544d3]891 conn->wdata.active = false;
[a35b458]892
[c7509e5]893 /*
894 * Note: the current fibril will be rescheduled either due to a
895 * timeout or due to an arriving message destined to it. In the
896 * former case, handle_expired_timeouts() and, in the latter
897 * case, route_call() will perform the wakeup.
898 */
[116d3f6f]899 fibril_switch(FIBRIL_TO_MANAGER);
[a35b458]900
[e70bfa5]901 /*
[c07544d3]902 * Futex is up after getting back from async_manager.
903 * Get it again.
[c7509e5]904 */
[49d072e]905 futex_down(&async_futex);
[3bacee1]906 if ((usecs) && (conn->wdata.to_event.occurred) &&
907 (list_empty(&conn->msg_queue))) {
[e70bfa5]908 /* If we timed out -> exit */
[49d072e]909 futex_up(&async_futex);
[01c3bb4]910 return CAP_NIL;
[49d072e]911 }
[450cd3a]912 }
[a35b458]913
[57dea62]914 msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
915 msg_t, link);
[80649a91]916 list_remove(&msg->link);
[a35b458]917
[eadaeae8]918 cap_call_handle_t chandle = msg->chandle;
[80649a91]919 *call = msg->call;
920 free(msg);
[a35b458]921
[01ff41c]922 futex_up(&async_futex);
[01c3bb4]923 return chandle;
[80649a91]924}
925
[455f190]926void *async_get_client_data(void)
927{
928 assert(fibril_connection);
929 return fibril_connection->client->data;
930}
931
[e2ab36f1]932void *async_get_client_data_by_id(task_id_t client_id)
[455f190]933{
[e2ab36f1]934 client_t *client = async_client_get(client_id, false);
[455f190]935 if (!client)
936 return NULL;
[a35b458]937
[455f190]938 if (!client->data) {
939 async_client_put(client);
940 return NULL;
941 }
[a35b458]942
[455f190]943 return client->data;
944}
945
[e2ab36f1]946void async_put_client_data_by_id(task_id_t client_id)
[455f190]947{
[e2ab36f1]948 client_t *client = async_client_get(client_id, false);
[a35b458]949
[455f190]950 assert(client);
951 assert(client->data);
[a35b458]952
[cdc8ee2d]953 /* Drop the reference we got in async_get_client_data_by_hash(). */
954 async_client_put(client);
[a35b458]955
[cdc8ee2d]956 /* Drop our own reference we got at the beginning of this function. */
[455f190]957 async_client_put(client);
958}
959
[36c9234]960/** Handle a call that was received.
961 *
962 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
963 * Otherwise the call is routed to its connection fibril.
964 *
[01c3bb4]965 * @param chandle Handle of the incoming call.
966 * @param call Data of the incoming call.
[6b21292]967 *
[36c9234]968 */
[eadaeae8]969static void handle_call(cap_call_handle_t chandle, ipc_call_t *call)
[80649a91]970{
[79ae36dd]971 assert(call);
[a35b458]972
[b688fd8]973 /* Kernel notification */
[addbce4]974 if ((chandle == CAP_NIL) && (call->flags & IPC_CALL_NOTIF)) {
[c170438]975 fibril_t *fibril = (fibril_t *) __tcb_get()->fibril_data;
976 unsigned oldsw = fibril->switches;
[a35b458]977
[01c3bb4]978 process_notification(call);
[a35b458]979
[c170438]980 if (oldsw != fibril->switches) {
981 /*
982 * The notification handler did not execute atomically
983 * and so the current manager fibril assumed the role of
984 * a notification fibril. While waiting for its
985 * resources, it switched to another manager fibril that
986 * had already existed or it created a new one. We
987 * therefore know there is at least yet another
988 * manager fibril that can take over. We now kill the
989 * current 'notification' fibril to prevent fibril
990 * population explosion.
991 */
992 futex_down(&async_futex);
993 fibril_switch(FIBRIL_FROM_DEAD);
994 }
[a35b458]995
[47b7006]996 return;
[6b21292]997 }
[a35b458]998
[566992e1]999 /* New connection */
1000 if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
1001 iface_t iface = (iface_t) IPC_GET_ARG1(*call);
1002 sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
[a35b458]1003
[49a796f1]1004 // TODO: Currently ignores all ports but the first one.
1005 void *data;
1006 async_port_handler_t handler =
1007 async_get_port_handler(iface, 0, &data);
[a35b458]1008
[01c3bb4]1009 async_new_connection(call->in_task_id, in_phone_hash, chandle,
[566992e1]1010 call, handler, data);
1011 return;
1012 }
[a35b458]1013
[36c9234]1014 /* Try to route the call through the connection hash table */
[01c3bb4]1015 if (route_call(chandle, call))
[47b7006]1016 return;
[a35b458]1017
[44c6d88d]1018 /* Unknown call from unknown phone - hang it up */
[01c3bb4]1019 ipc_answer_0(chandle, EHANGUP);
[450cd3a]1020}
1021
[f2f0392]1022/** Fire all timeouts that expired. */
[c042bdd]1023static void handle_expired_timeouts(void)
1024{
1025 struct timeval tv;
[45cbcaf4]1026 getuptime(&tv);
[a35b458]1027
[c042bdd]1028 futex_down(&async_futex);
[a35b458]1029
[b72efe8]1030 link_t *cur = list_first(&timeout_list);
1031 while (cur != NULL) {
[47b7006]1032 awaiter_t *waiter =
1033 list_get_instance(cur, awaiter_t, to_event.link);
[a35b458]1034
[f53cc81]1035 if (tv_gt(&waiter->to_event.expires, &tv))
[c042bdd]1036 break;
[a35b458]1037
[f53cc81]1038 list_remove(&waiter->to_event.link);
1039 waiter->to_event.inlist = false;
1040 waiter->to_event.occurred = true;
[a35b458]1041
[36c9234]1042 /*
[c07544d3]1043 * Redundant condition?
1044 * The fibril should not be active when it gets here.
[c042bdd]1045 */
[49d072e]1046 if (!waiter->active) {
[c07544d3]1047 waiter->active = true;
[bc1f1c2]1048 fibril_add_ready(waiter->fid);
[c042bdd]1049 }
[a35b458]1050
[b72efe8]1051 cur = list_first(&timeout_list);
[c042bdd]1052 }
[a35b458]1053
[c042bdd]1054 futex_up(&async_futex);
1055}
1056
[36c9234]1057/** Endless loop dispatching incoming calls and answers.
1058 *
[c07544d3]1059 * @return Never returns.
1060 *
[36c9234]1061 */
[b7fd2a0]1062static errno_t async_manager_worker(void)
[80649a91]1063{
[c07544d3]1064 while (true) {
[116d3f6f]1065 if (fibril_switch(FIBRIL_FROM_MANAGER)) {
[47b7006]1066 futex_up(&async_futex);
[36c9234]1067 /*
1068 * async_futex is always held when entering a manager
1069 * fibril.
[a46da63]1070 */
[80649a91]1071 continue;
1072 }
[a35b458]1073
[c042bdd]1074 futex_down(&async_futex);
[a35b458]1075
[c07544d3]1076 suseconds_t timeout;
[1db6dfd]1077 unsigned int flags = SYNCH_FLAGS_NONE;
[c042bdd]1078 if (!list_empty(&timeout_list)) {
[b72efe8]1079 awaiter_t *waiter = list_get_instance(
1080 list_first(&timeout_list), awaiter_t, to_event.link);
[a35b458]1081
[c07544d3]1082 struct timeval tv;
[45cbcaf4]1083 getuptime(&tv);
[a35b458]1084
[f53cc81]1085 if (tv_gteq(&tv, &waiter->to_event.expires)) {
[6c46350]1086 futex_up(&async_futex);
[c042bdd]1087 handle_expired_timeouts();
[1db6dfd]1088 /*
1089 * Notice that even if the event(s) already
1090 * expired (and thus the other fibril was
1091 * supposed to be running already),
1092 * we check for incoming IPC.
1093 *
1094 * Otherwise, a fibril that continuously
1095 * creates (almost) expired events could
1096 * prevent IPC retrieval from the kernel.
1097 */
1098 timeout = 0;
1099 flags = SYNCH_FLAGS_NON_BLOCKING;
1100
1101 } else {
[7f9d97f3]1102 timeout = tv_sub_diff(&waiter->to_event.expires,
1103 &tv);
[1db6dfd]1104 futex_up(&async_futex);
1105 }
1106 } else {
1107 futex_up(&async_futex);
[0b99e40]1108 timeout = SYNCH_NO_TIMEOUT;
[1db6dfd]1109 }
[a35b458]1110
[8619f25]1111 atomic_inc(&threads_in_ipc_wait);
[a35b458]1112
[c07544d3]1113 ipc_call_t call;
[b7fd2a0]1114 errno_t rc = ipc_wait_cycle(&call, timeout, flags);
[a35b458]1115
[8619f25]1116 atomic_dec(&threads_in_ipc_wait);
[a35b458]1117
[6deb2cd]1118 assert(rc == EOK);
[01c3bb4]1119
[6deb2cd]1120 if (call.cap_handle == CAP_NIL) {
[a1026da]1121 if ((call.flags &
1122 (IPC_CALL_NOTIF | IPC_CALL_ANSWERED)) == 0) {
1123 /* Neither a notification nor an answer. */
[01c3bb4]1124 handle_expired_timeouts();
1125 continue;
1126 }
[0b99e40]1127 }
[01c3bb4]1128
[addbce4]1129 if (call.flags & IPC_CALL_ANSWERED)
[80649a91]1130 continue;
[01c3bb4]1131
[6deb2cd]1132 handle_call(call.cap_handle, &call);
[80649a91]1133 }
[01c3bb4]1134
[a46da63]1135 return 0;
[80649a91]1136}
1137
[36c9234]1138/** Function to start async_manager as a standalone fibril.
[c07544d3]1139 *
[36c9234]1140 * When more kernel threads are used, one async manager should exist per thread.
1141 *
[c07544d3]1142 * @param arg Unused.
1143 * @return Never returns.
[36c9234]1144 *
[a2cd194]1145 */
[b7fd2a0]1146static errno_t async_manager_fibril(void *arg)
[80649a91]1147{
[a46da63]1148 futex_up(&async_futex);
[a35b458]1149
[36c9234]1150 /*
1151 * async_futex is always locked when entering manager
1152 */
[085bd54]1153 async_manager_worker();
[a35b458]1154
[a46da63]1155 return 0;
[80649a91]1156}
[450cd3a]1157
[36c9234]1158/** Add one manager to manager list. */
[80649a91]1159void async_create_manager(void)
[450cd3a]1160{
[c170438]1161 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
[86d7bfa]1162 if (fid != 0)
1163 fibril_add_manager(fid);
[80649a91]1164}
1165
1166/** Remove one manager from manager list */
1167void async_destroy_manager(void)
1168{
[bc1f1c2]1169 fibril_remove_manager();
[80649a91]1170}
1171
[36c9234]1172/** Initialize the async framework.
1173 *
1174 */
[49a796f1]1175void __async_server_init(void)
[80649a91]1176{
[062d900]1177 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
[47b7006]1178 abort();
[a35b458]1179
[062d900]1180 if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
[47b7006]1181 abort();
[a35b458]1182
[8820544]1183 if (!hash_table_create(&notification_hash_table, 0, 0,
1184 &notification_hash_table_ops))
1185 abort();
[49a796f1]1186}
[a35b458]1187
[49a796f1]1188errno_t async_answer_0(cap_call_handle_t chandle, errno_t retval)
1189{
1190 return ipc_answer_0(chandle, retval);
[450cd3a]1191}
[01ff41c]1192
[49a796f1]1193errno_t async_answer_1(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1)
[01ff41c]1194{
[49a796f1]1195 return ipc_answer_1(chandle, retval, arg1);
1196}
[a35b458]1197
[49a796f1]1198errno_t async_answer_2(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
1199 sysarg_t arg2)
1200{
1201 return ipc_answer_2(chandle, retval, arg1, arg2);
1202}
[a35b458]1203
[49a796f1]1204errno_t async_answer_3(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
1205 sysarg_t arg2, sysarg_t arg3)
1206{
1207 return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
1208}
[a35b458]1209
[49a796f1]1210errno_t async_answer_4(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
1211 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1212{
1213 return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
1214}
[a35b458]1215
[49a796f1]1216errno_t async_answer_5(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
1217 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
1218{
1219 return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
1220}
[a35b458]1221
[49a796f1]1222errno_t async_forward_fast(cap_call_handle_t chandle, async_exch_t *exch,
1223 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1224{
1225 if (exch == NULL)
1226 return ENOENT;
[a35b458]1227
[49a796f1]1228 return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2, mode);
1229}
[a35b458]1230
[49a796f1]1231errno_t async_forward_slow(cap_call_handle_t chandle, async_exch_t *exch,
1232 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1233 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
1234{
1235 if (exch == NULL)
1236 return ENOENT;
[a35b458]1237
[49a796f1]1238 return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
1239 arg4, arg5, mode);
[01ff41c]1240}
1241
[49a796f1]1242/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
[36c9234]1243 *
[49a796f1]1244 * Ask through phone for a new connection to some service.
[01ff41c]1245 *
[49a796f1]1246 * @param exch Exchange for sending the message.
1247 * @param arg1 User defined argument.
1248 * @param arg2 User defined argument.
1249 * @param arg3 User defined argument.
[c07544d3]1250 *
[49a796f1]1251 * @return Zero on success or an error code.
[36c9234]1252 *
[01ff41c]1253 */
[49a796f1]1254errno_t async_connect_to_me(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
1255 sysarg_t arg3)
[01ff41c]1256{
[79ae36dd]1257 if (exch == NULL)
[49a796f1]1258 return ENOENT;
[a35b458]1259
[49a796f1]1260 ipc_call_t answer;
1261 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
1262 &answer);
[a35b458]1263
[49a796f1]1264 errno_t rc;
1265 async_wait_for(req, &rc);
1266 if (rc != EOK)
1267 return (errno_t) rc;
[a35b458]1268
[49a796f1]1269 return EOK;
1270}
[a35b458]1271
[49a796f1]1272/** Interrupt one thread of this task from waiting for IPC. */
1273void async_poke(void)
1274{
[c8afd5a]1275 if (atomic_get(&threads_in_ipc_wait) > 0)
1276 ipc_poke();
[01ff41c]1277}
1278
[49a796f1]1279/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
[0da4e41]1280 *
[47b7006]1281 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
1282 * calls so that the user doesn't have to remember the meaning of each IPC
1283 * argument.
[0da4e41]1284 *
1285 * So far, this wrapper is to be used from within a connection fibril.
1286 *
[01c3bb4]1287 * @param chandle Storage for the handle of the IPC_M_SHARE_IN call.
1288 * @param size Destination address space area size.
[47b7006]1289 *
1290 * @return True on success, false on failure.
[0da4e41]1291 *
1292 */
[eadaeae8]1293bool async_share_in_receive(cap_call_handle_t *chandle, size_t *size)
[0da4e41]1294{
[01c3bb4]1295 assert(chandle);
[0da4e41]1296 assert(size);
[a35b458]1297
[47b7006]1298 ipc_call_t data;
[01c3bb4]1299 *chandle = async_get_call(&data);
[a35b458]1300
[228e490]1301 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
[47b7006]1302 return false;
[a35b458]1303
[fbcdeb8]1304 *size = (size_t) IPC_GET_ARG1(data);
[47b7006]1305 return true;
[0da4e41]1306}
1307
1308/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1309 *
[fbcdeb8]1310 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
[47b7006]1311 * calls so that the user doesn't have to remember the meaning of each IPC
1312 * argument.
[0da4e41]1313 *
[01c3bb4]1314 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
1315 * @param src Source address space base.
1316 * @param flags Flags to be used for sharing. Bits can be only cleared.
[47b7006]1317 *
1318 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1319 *
1320 */
[eadaeae8]1321errno_t async_share_in_finalize(cap_call_handle_t chandle, void *src,
1322 unsigned int flags)
[0da4e41]1323{
[a69d42e]1324 // FIXME: The source has no business deciding destination address.
[01c3bb4]1325 return ipc_answer_3(chandle, EOK, (sysarg_t) src, (sysarg_t) flags,
[a69d42e]1326 (sysarg_t) _end);
[0da4e41]1327}
1328
1329/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1330 *
[47b7006]1331 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
1332 * calls so that the user doesn't have to remember the meaning of each IPC
1333 * argument.
[0da4e41]1334 *
1335 * So far, this wrapper is to be used from within a connection fibril.
1336 *
[01c3bb4]1337 * @param chandle Storage for the hash of the IPC_M_SHARE_OUT call.
1338 * @param size Storage for the source address space area size.
1339 * @param flags Storage for the sharing flags.
[47b7006]1340 *
1341 * @return True on success, false on failure.
[0da4e41]1342 *
1343 */
[eadaeae8]1344bool async_share_out_receive(cap_call_handle_t *chandle, size_t *size,
[01c3bb4]1345 unsigned int *flags)
[0da4e41]1346{
[01c3bb4]1347 assert(chandle);
[0da4e41]1348 assert(size);
1349 assert(flags);
[a35b458]1350
[47b7006]1351 ipc_call_t data;
[01c3bb4]1352 *chandle = async_get_call(&data);
[a35b458]1353
[228e490]1354 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
[47b7006]1355 return false;
[a35b458]1356
[0da4e41]1357 *size = (size_t) IPC_GET_ARG2(data);
[47b7006]1358 *flags = (unsigned int) IPC_GET_ARG3(data);
1359 return true;
[0da4e41]1360}
1361
1362/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1363 *
[47b7006]1364 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
1365 * calls so that the user doesn't have to remember the meaning of each IPC
1366 * argument.
[0da4e41]1367 *
[01c3bb4]1368 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
1369 * @param dst Address of the storage for the destination address space area
1370 * base address.
[47b7006]1371 *
[01c3bb4]1372 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1373 *
1374 */
[eadaeae8]1375errno_t async_share_out_finalize(cap_call_handle_t chandle, void **dst)
[0da4e41]1376{
[a69d42e]1377 return ipc_answer_2(chandle, EOK, (sysarg_t) _end, (sysarg_t) dst);
[0da4e41]1378}
1379
1380/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1381 *
[47b7006]1382 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
1383 * calls so that the user doesn't have to remember the meaning of each IPC
1384 * argument.
[0da4e41]1385 *
1386 * So far, this wrapper is to be used from within a connection fibril.
1387 *
[01c3bb4]1388 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
1389 * @param size Storage for the maximum size. Can be NULL.
[47b7006]1390 *
1391 * @return True on success, false on failure.
[0da4e41]1392 *
1393 */
[eadaeae8]1394bool async_data_read_receive(cap_call_handle_t *chandle, size_t *size)
[d768d4c8]1395{
1396 ipc_call_t data;
[01c3bb4]1397 return async_data_read_receive_call(chandle, &data, size);
[d768d4c8]1398}
1399
1400/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1401 *
1402 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
1403 * calls so that the user doesn't have to remember the meaning of each IPC
1404 * argument.
1405 *
1406 * So far, this wrapper is to be used from within a connection fibril.
1407 *
[01c3bb4]1408 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
1409 * @param size Storage for the maximum size. Can be NULL.
[d768d4c8]1410 *
1411 * @return True on success, false on failure.
1412 *
1413 */
[eadaeae8]1414bool async_data_read_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
[d768d4c8]1415 size_t *size)
[0da4e41]1416{
[01c3bb4]1417 assert(chandle);
[d768d4c8]1418 assert(data);
[a35b458]1419
[01c3bb4]1420 *chandle = async_get_call(data);
[a35b458]1421
[d768d4c8]1422 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
[47b7006]1423 return false;
[a35b458]1424
[0da4e41]1425 if (size)
[d768d4c8]1426 *size = (size_t) IPC_GET_ARG2(*data);
[a35b458]1427
[47b7006]1428 return true;
[0da4e41]1429}
1430
1431/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
1432 *
[47b7006]1433 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
1434 * calls so that the user doesn't have to remember the meaning of each IPC
1435 * argument.
[0da4e41]1436 *
[01c3bb4]1437 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
1438 * @param src Source address for the IPC_M_DATA_READ call.
1439 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
1440 * the maximum size announced by the sender.
[47b7006]1441 *
[01c3bb4]1442 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1443 *
1444 */
[eadaeae8]1445errno_t async_data_read_finalize(cap_call_handle_t chandle, const void *src,
1446 size_t size)
[0da4e41]1447{
[01c3bb4]1448 return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
[0da4e41]1449}
1450
[b4cbef1]1451/** Wrapper for forwarding any read request
1452 *
1453 */
[b7fd2a0]1454errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
[79ae36dd]1455 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1456 ipc_call_t *dataptr)
[b4cbef1]1457{
[79ae36dd]1458 if (exch == NULL)
1459 return ENOENT;
[a35b458]1460
[eadaeae8]1461 cap_call_handle_t chandle;
[01c3bb4]1462 if (!async_data_read_receive(&chandle, NULL)) {
1463 ipc_answer_0(chandle, EINVAL);
[b4cbef1]1464 return EINVAL;
1465 }
[a35b458]1466
[79ae36dd]1467 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]1468 dataptr);
1469 if (msg == 0) {
[01c3bb4]1470 ipc_answer_0(chandle, EINVAL);
[b4cbef1]1471 return EINVAL;
1472 }
[a35b458]1473
[b7fd2a0]1474 errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
[b4cbef1]1475 IPC_FF_ROUTE_FROM_ME);
1476 if (retval != EOK) {
[ab9f443]1477 async_forget(msg);
[01c3bb4]1478 ipc_answer_0(chandle, retval);
[b4cbef1]1479 return retval;
1480 }
[a35b458]1481
[b7fd2a0]1482 errno_t rc;
[b4cbef1]1483 async_wait_for(msg, &rc);
[a35b458]1484
[b7fd2a0]1485 return (errno_t) rc;
[b4cbef1]1486}
1487
[0da4e41]1488/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1489 *
[47b7006]1490 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
1491 * calls so that the user doesn't have to remember the meaning of each IPC
1492 * argument.
[0da4e41]1493 *
1494 * So far, this wrapper is to be used from within a connection fibril.
1495 *
[01c3bb4]1496 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
1497 * @param size Storage for the suggested size. May be NULL.
[b4cbef1]1498 *
[01c3bb4]1499 * @return True on success, false on failure.
[0da4e41]1500 *
1501 */
[eadaeae8]1502bool async_data_write_receive(cap_call_handle_t *chandle, size_t *size)
[5ae1c51]1503{
1504 ipc_call_t data;
[01c3bb4]1505 return async_data_write_receive_call(chandle, &data, size);
[5ae1c51]1506}
1507
1508/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1509 *
1510 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
1511 * calls so that the user doesn't have to remember the meaning of each IPC
1512 * argument.
1513 *
1514 * So far, this wrapper is to be used from within a connection fibril.
1515 *
[01c3bb4]1516 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
1517 * @param data Storage for the ipc call data.
1518 * @param size Storage for the suggested size. May be NULL.
[5ae1c51]1519 *
1520 * @return True on success, false on failure.
1521 *
1522 */
[eadaeae8]1523bool async_data_write_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
[5ae1c51]1524 size_t *size)
[0da4e41]1525{
[01c3bb4]1526 assert(chandle);
[5ae1c51]1527 assert(data);
[a35b458]1528
[01c3bb4]1529 *chandle = async_get_call(data);
[a35b458]1530
[5ae1c51]1531 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
[47b7006]1532 return false;
[a35b458]1533
[0da4e41]1534 if (size)
[5ae1c51]1535 *size = (size_t) IPC_GET_ARG2(*data);
[a35b458]1536
[47b7006]1537 return true;
[0da4e41]1538}
1539
1540/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
1541 *
[47b7006]1542 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
1543 * calls so that the user doesn't have to remember the meaning of each IPC
1544 * argument.
[0da4e41]1545 *
[01c3bb4]1546 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
1547 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
1548 * @param size Final size for the IPC_M_DATA_WRITE call.
[b4cbef1]1549 *
[01c3bb4]1550 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1551 *
1552 */
[eadaeae8]1553errno_t async_data_write_finalize(cap_call_handle_t chandle, void *dst,
1554 size_t size)
[0da4e41]1555{
[01c3bb4]1556 return ipc_answer_2(chandle, EOK, (sysarg_t) dst, (sysarg_t) size);
[0da4e41]1557}
1558
[eda925a]1559/** Wrapper for receiving binary data or strings
[8aa42e3]1560 *
1561 * This wrapper only makes it more comfortable to use async_data_write_*
[eda925a]1562 * functions to receive binary data or strings.
[8aa42e3]1563 *
[472c09d]1564 * @param data Pointer to data pointer (which should be later disposed
1565 * by free()). If the operation fails, the pointer is not
1566 * touched.
[eda925a]1567 * @param nullterm If true then the received data is always zero terminated.
1568 * This also causes to allocate one extra byte beyond the
1569 * raw transmitted data.
[b4cbef1]1570 * @param min_size Minimum size (in bytes) of the data to receive.
[472c09d]1571 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
1572 * no limit.
[eda925a]1573 * @param granulariy If non-zero then the size of the received data has to
[472c09d]1574 * be divisible by this value.
1575 * @param received If not NULL, the size of the received data is stored here.
[8aa42e3]1576 *
1577 * @return Zero on success or a value from @ref errno.h on failure.
1578 *
1579 */
[b7fd2a0]1580errno_t async_data_write_accept(void **data, const bool nullterm,
[eda925a]1581 const size_t min_size, const size_t max_size, const size_t granularity,
1582 size_t *received)
[8aa42e3]1583{
[79ae36dd]1584 assert(data);
[a35b458]1585
[eadaeae8]1586 cap_call_handle_t chandle;
[8aa42e3]1587 size_t size;
[01c3bb4]1588 if (!async_data_write_receive(&chandle, &size)) {
1589 ipc_answer_0(chandle, EINVAL);
[8aa42e3]1590 return EINVAL;
1591 }
[a35b458]1592
[b4cbef1]1593 if (size < min_size) {
[01c3bb4]1594 ipc_answer_0(chandle, EINVAL);
[b4cbef1]1595 return EINVAL;
1596 }
[a35b458]1597
[8aa42e3]1598 if ((max_size > 0) && (size > max_size)) {
[01c3bb4]1599 ipc_answer_0(chandle, EINVAL);
[8aa42e3]1600 return EINVAL;
1601 }
[a35b458]1602
[472c09d]1603 if ((granularity > 0) && ((size % granularity) != 0)) {
[01c3bb4]1604 ipc_answer_0(chandle, EINVAL);
[472c09d]1605 return EINVAL;
1606 }
[a35b458]1607
[57dea62]1608 void *arg_data;
[a35b458]1609
[eda925a]1610 if (nullterm)
[57dea62]1611 arg_data = malloc(size + 1);
[eda925a]1612 else
[57dea62]1613 arg_data = malloc(size);
[a35b458]1614
[57dea62]1615 if (arg_data == NULL) {
[01c3bb4]1616 ipc_answer_0(chandle, ENOMEM);
[8aa42e3]1617 return ENOMEM;
1618 }
[a35b458]1619
[b7fd2a0]1620 errno_t rc = async_data_write_finalize(chandle, arg_data, size);
[8aa42e3]1621 if (rc != EOK) {
[57dea62]1622 free(arg_data);
[8aa42e3]1623 return rc;
1624 }
[a35b458]1625
[eda925a]1626 if (nullterm)
[57dea62]1627 ((char *) arg_data)[size] = 0;
[a35b458]1628
[57dea62]1629 *data = arg_data;
[472c09d]1630 if (received != NULL)
1631 *received = size;
[a35b458]1632
[8aa42e3]1633 return EOK;
1634}
1635
[b4cbef1]1636/** Wrapper for voiding any data that is about to be received
1637 *
1638 * This wrapper can be used to void any pending data
1639 *
1640 * @param retval Error value from @ref errno.h to be returned to the caller.
1641 *
1642 */
[b7fd2a0]1643void async_data_write_void(errno_t retval)
[b4cbef1]1644{
[eadaeae8]1645 cap_call_handle_t chandle;
[01c3bb4]1646 async_data_write_receive(&chandle, NULL);
1647 ipc_answer_0(chandle, retval);
[b4cbef1]1648}
1649
1650/** Wrapper for forwarding any data that is about to be received
1651 *
1652 */
[b7fd2a0]1653errno_t async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
[79ae36dd]1654 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1655 ipc_call_t *dataptr)
[b4cbef1]1656{
[79ae36dd]1657 if (exch == NULL)
1658 return ENOENT;
[a35b458]1659
[eadaeae8]1660 cap_call_handle_t chandle;
[01c3bb4]1661 if (!async_data_write_receive(&chandle, NULL)) {
1662 ipc_answer_0(chandle, EINVAL);
[b4cbef1]1663 return EINVAL;
1664 }
[a35b458]1665
[79ae36dd]1666 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]1667 dataptr);
1668 if (msg == 0) {
[01c3bb4]1669 ipc_answer_0(chandle, EINVAL);
[b4cbef1]1670 return EINVAL;
1671 }
[a35b458]1672
[b7fd2a0]1673 errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
[b4cbef1]1674 IPC_FF_ROUTE_FROM_ME);
1675 if (retval != EOK) {
[ab9f443]1676 async_forget(msg);
[01c3bb4]1677 ipc_answer_0(chandle, retval);
[b4cbef1]1678 return retval;
1679 }
[a35b458]1680
[b7fd2a0]1681 errno_t rc;
[b4cbef1]1682 async_wait_for(msg, &rc);
[a35b458]1683
[b7fd2a0]1684 return (errno_t) rc;
[b4cbef1]1685}
1686
[79ae36dd]1687/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1688 *
1689 * If the current call is IPC_M_CONNECT_TO_ME then a new
1690 * async session is created for the accepted phone.
1691 *
1692 * @param mgmt Exchange management style.
1693 *
[8869f7b]1694 * @return New async session.
1695 * @return NULL on failure.
[79ae36dd]1696 *
1697 */
1698async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
1699{
1700 /* Accept the phone */
1701 ipc_call_t call;
[eadaeae8]1702 cap_call_handle_t chandle = async_get_call(&call);
1703 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
[a35b458]1704
[eadaeae8]1705 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) ||
1706 !CAP_HANDLE_VALID((phandle))) {
[01c3bb4]1707 async_answer_0(chandle, EINVAL);
[79ae36dd]1708 return NULL;
1709 }
[a35b458]1710
[79ae36dd]1711 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1712 if (sess == NULL) {
[01c3bb4]1713 async_answer_0(chandle, ENOMEM);
[79ae36dd]1714 return NULL;
1715 }
[a35b458]1716
[566992e1]1717 sess->iface = 0;
[79ae36dd]1718 sess->mgmt = mgmt;
[01c3bb4]1719 sess->phone = phandle;
[79ae36dd]1720 sess->arg1 = 0;
1721 sess->arg2 = 0;
1722 sess->arg3 = 0;
[a35b458]1723
[58cbf8d5]1724 fibril_mutex_initialize(&sess->remote_state_mtx);
1725 sess->remote_state_data = NULL;
[a35b458]1726
[79ae36dd]1727 list_initialize(&sess->exch_list);
1728 fibril_mutex_initialize(&sess->mutex);
1729 atomic_set(&sess->refcnt, 0);
[a35b458]1730
[79ae36dd]1731 /* Acknowledge the connected phone */
[01c3bb4]1732 async_answer_0(chandle, EOK);
[a35b458]1733
[79ae36dd]1734 return sess;
1735}
1736
[8869f7b]1737/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1738 *
1739 * If the call is IPC_M_CONNECT_TO_ME then a new
1740 * async session is created. However, the phone is
1741 * not accepted automatically.
1742 *
1743 * @param mgmt Exchange management style.
1744 * @param call Call data.
1745 *
1746 * @return New async session.
1747 * @return NULL on failure.
1748 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
1749 *
1750 */
1751async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
1752{
[eadaeae8]1753 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
[a35b458]1754
[eadaeae8]1755 if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) ||
1756 !CAP_HANDLE_VALID((phandle)))
[8869f7b]1757 return NULL;
[a35b458]1758
[8869f7b]1759 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1760 if (sess == NULL)
1761 return NULL;
[a35b458]1762
[566992e1]1763 sess->iface = 0;
[8869f7b]1764 sess->mgmt = mgmt;
[01c3bb4]1765 sess->phone = phandle;
[8869f7b]1766 sess->arg1 = 0;
1767 sess->arg2 = 0;
1768 sess->arg3 = 0;
[a35b458]1769
[58cbf8d5]1770 fibril_mutex_initialize(&sess->remote_state_mtx);
1771 sess->remote_state_data = NULL;
[a35b458]1772
[8869f7b]1773 list_initialize(&sess->exch_list);
1774 fibril_mutex_initialize(&sess->mutex);
1775 atomic_set(&sess->refcnt, 0);
[a35b458]1776
[8869f7b]1777 return sess;
1778}
1779
[eadaeae8]1780bool async_state_change_receive(cap_call_handle_t *chandle, sysarg_t *arg1,
[2c4aa39]1781 sysarg_t *arg2, sysarg_t *arg3)
1782{
[01c3bb4]1783 assert(chandle);
[a35b458]1784
[2c4aa39]1785 ipc_call_t call;
[01c3bb4]1786 *chandle = async_get_call(&call);
[a35b458]1787
[2c4aa39]1788 if (IPC_GET_IMETHOD(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
1789 return false;
[a35b458]1790
[2c4aa39]1791 if (arg1)
1792 *arg1 = IPC_GET_ARG1(call);
1793 if (arg2)
1794 *arg2 = IPC_GET_ARG2(call);
1795 if (arg3)
1796 *arg3 = IPC_GET_ARG3(call);
[a35b458]1797
[2c4aa39]1798 return true;
1799}
1800
[eadaeae8]1801errno_t async_state_change_finalize(cap_call_handle_t chandle,
1802 async_exch_t *other_exch)
[2c4aa39]1803{
[eadaeae8]1804 return ipc_answer_1(chandle, EOK, CAP_HANDLE_RAW(other_exch->phone));
[2c4aa39]1805}
1806
[a46da63]1807/** @}
[b2951e2]1808 */
Note: See TracBrowser for help on using the repository browser.