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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d73d992 was d73d992, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Hide libc-internal details of the fibril implementation.

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