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

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

Turn ipc_get_*/ipc_set_* into functions

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