source: mainline/uspace/lib/c/generic/async.c@ ae84e38

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ae84e38 was eadaeae8, checked in by Jakub Jermar <jakub@…>, 8 years ago

Make capability handles type-safe

Define distinct pointer types for the handles of the supported
capability types and use them instead of integer handles. This makes it
virtually impossible to pass a non-handle or a handle of different type
instead of the proper handle. Also turn cap_handle_t into an "untyped"
capability handle that can be assigned to and from the "typed" handles.

This commit also fixes a bug in msim-con driver, which wrongly used the
IRQ number instead of the IRQ capability handle to unregister the IRQ.

This commit also fixes the wrong use of the capability handle instead
of error code in libusbhost.

  • Property mode set to 100644
File size: 80.1 KB
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
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.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/** @file
33 */
34
35/**
36 * Asynchronous library
37 *
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.
41 *
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.
45 *
46 * Example of use (pseudo C):
47 *
48 * 1) Multithreaded client application
49 *
50 * fibril_create(fibril1, ...);
51 * fibril_create(fibril2, ...);
52 * ...
53 *
54 * int fibril1(void *arg)
55 * {
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 *
66 * async_wait_for(c1);
67 * async_wait_for(c2);
68 * ...
69 * }
70 *
71 *
72 * 2) Multithreaded server application
73 *
74 * main()
75 * {
76 * async_manager();
77 * }
78 *
79 * port_handler(ichandle, *icall)
80 * {
81 * if (want_refuse) {
82 * async_answer_0(ichandle, ELIMIT);
83 * return;
84 * }
85 * async_answer_0(ichandle, EOK);
86 *
87 * chandle = async_get_call(&call);
88 * somehow_handle_the_call(chandle, call);
89 * async_answer_2(chandle, 1, 2, 3);
90 *
91 * chandle = async_get_call(&call);
92 * ...
93 * }
94 *
95 */
96
97#define LIBC_ASYNC_C_
98#include <ipc/ipc.h>
99#include <async.h>
100#include "private/async.h"
101#undef LIBC_ASYNC_C_
102
103#include <ipc/irq.h>
104#include <ipc/event.h>
105#include <futex.h>
106#include <fibril.h>
107#include <adt/hash_table.h>
108#include <adt/hash.h>
109#include <adt/list.h>
110#include <assert.h>
111#include <errno.h>
112#include <sys/time.h>
113#include <libarch/barrier.h>
114#include <stdbool.h>
115#include <stdlib.h>
116#include <mem.h>
117#include <stdlib.h>
118#include <macros.h>
119#include <as.h>
120#include <abi/mm/as.h>
121#include "private/libc.h"
122
123/** Session data */
124struct async_sess {
125 /** List of inactive exchanges */
126 list_t exch_list;
127
128 /** Session interface */
129 iface_t iface;
130
131 /** Exchange management style */
132 exch_mgmt_t mgmt;
133
134 /** Session identification */
135 cap_phone_handle_t phone;
136
137 /** First clone connection argument */
138 sysarg_t arg1;
139
140 /** Second clone connection argument */
141 sysarg_t arg2;
142
143 /** Third clone connection argument */
144 sysarg_t arg3;
145
146 /** Exchange mutex */
147 fibril_mutex_t mutex;
148
149 /** Number of opened exchanges */
150 atomic_t refcnt;
151
152 /** Mutex for stateful connections */
153 fibril_mutex_t remote_state_mtx;
154
155 /** Data for stateful connections */
156 void *remote_state_data;
157};
158
159/** Exchange data */
160struct async_exch {
161 /** Link into list of inactive exchanges */
162 link_t sess_link;
163
164 /** Link into global list of inactive exchanges */
165 link_t global_link;
166
167 /** Session pointer */
168 async_sess_t *sess;
169
170 /** Exchange identification */
171 cap_phone_handle_t phone;
172};
173
174/** Async framework global futex */
175futex_t async_futex = FUTEX_INITIALIZER;
176
177/** Number of threads waiting for IPC in the kernel. */
178atomic_t threads_in_ipc_wait = { 0 };
179
180/** Naming service session */
181async_sess_t *session_ns;
182
183/** Call data */
184typedef struct {
185 link_t link;
186
187 cap_call_handle_t chandle;
188 ipc_call_t call;
189} msg_t;
190
191/** Message data */
192typedef struct {
193 awaiter_t wdata;
194
195 /** If reply was received. */
196 bool done;
197
198 /** If the message / reply should be discarded on arrival. */
199 bool forget;
200
201 /** If already destroyed. */
202 bool destroyed;
203
204 /** Pointer to where the answer data is stored. */
205 ipc_call_t *dataptr;
206
207 errno_t retval;
208} amsg_t;
209
210/* Client connection data */
211typedef struct {
212 ht_link_t link;
213
214 task_id_t in_task_id;
215 atomic_t refcnt;
216 void *data;
217} client_t;
218
219/* Server connection data */
220typedef struct {
221 awaiter_t wdata;
222
223 /** Hash table link. */
224 ht_link_t link;
225
226 /** Incoming client task ID. */
227 task_id_t in_task_id;
228
229 /** Incoming phone hash. */
230 sysarg_t in_phone_hash;
231
232 /** Link to the client tracking structure. */
233 client_t *client;
234
235 /** Messages that should be delivered to this fibril. */
236 list_t msg_queue;
237
238 /** Identification of the opening call. */
239 cap_call_handle_t chandle;
240
241 /** Call data of the opening call. */
242 ipc_call_t call;
243
244 /** Identification of the closing call. */
245 cap_call_handle_t close_chandle;
246
247 /** Fibril function that will be used to handle the connection. */
248 async_port_handler_t handler;
249
250 /** Client data */
251 void *data;
252} connection_t;
253
254/** Interface data */
255typedef struct {
256 ht_link_t link;
257
258 /** Interface ID */
259 iface_t iface;
260
261 /** Futex protecting the hash table */
262 futex_t futex;
263
264 /** Interface ports */
265 hash_table_t port_hash_table;
266
267 /** Next available port ID */
268 port_id_t port_id_avail;
269} interface_t;
270
271/* Port data */
272typedef struct {
273 ht_link_t link;
274
275 /** Port ID */
276 port_id_t id;
277
278 /** Port connection handler */
279 async_port_handler_t handler;
280
281 /** Client data */
282 void *data;
283} port_t;
284
285/* Notification data */
286typedef struct {
287 ht_link_t link;
288
289 /** Notification method */
290 sysarg_t imethod;
291
292 /** Notification handler */
293 async_notification_handler_t handler;
294
295 /** Notification data */
296 void *data;
297} notification_t;
298
299/** Identifier of the incoming connection handled by the current fibril. */
300static fibril_local connection_t *fibril_connection;
301
302static void to_event_initialize(to_event_t *to)
303{
304 struct timeval tv = { 0, 0 };
305
306 to->inlist = false;
307 to->occurred = false;
308 link_initialize(&to->link);
309 to->expires = tv;
310}
311
312static void wu_event_initialize(wu_event_t *wu)
313{
314 wu->inlist = false;
315 link_initialize(&wu->link);
316}
317
318void awaiter_initialize(awaiter_t *aw)
319{
320 aw->fid = 0;
321 aw->active = false;
322 to_event_initialize(&aw->to_event);
323 wu_event_initialize(&aw->wu_event);
324}
325
326static amsg_t *amsg_create(void)
327{
328 amsg_t *msg = malloc(sizeof(amsg_t));
329 if (msg) {
330 msg->done = false;
331 msg->forget = false;
332 msg->destroyed = false;
333 msg->dataptr = NULL;
334 msg->retval = EINVAL;
335 awaiter_initialize(&msg->wdata);
336 }
337
338 return msg;
339}
340
341static void amsg_destroy(amsg_t *msg)
342{
343 assert(!msg->destroyed);
344 msg->destroyed = true;
345 free(msg);
346}
347
348static void *default_client_data_constructor(void)
349{
350 return NULL;
351}
352
353static void default_client_data_destructor(void *data)
354{
355}
356
357static async_client_data_ctor_t async_client_data_create =
358 default_client_data_constructor;
359static async_client_data_dtor_t async_client_data_destroy =
360 default_client_data_destructor;
361
362void async_set_client_data_constructor(async_client_data_ctor_t ctor)
363{
364 assert(async_client_data_create == default_client_data_constructor);
365 async_client_data_create = ctor;
366}
367
368void async_set_client_data_destructor(async_client_data_dtor_t dtor)
369{
370 assert(async_client_data_destroy == default_client_data_destructor);
371 async_client_data_destroy = dtor;
372}
373
374/** Default fallback fibril function.
375 *
376 * This fallback fibril function gets called on incomming connections that do
377 * not have a specific handler defined.
378 *
379 * @param chandle Handle of the incoming call.
380 * @param call Data of the incoming call.
381 * @param arg Local argument
382 *
383 */
384static void default_fallback_port_handler(cap_call_handle_t chandle,
385 ipc_call_t *call, void *arg)
386{
387 ipc_answer_0(chandle, ENOENT);
388}
389
390static async_port_handler_t fallback_port_handler =
391 default_fallback_port_handler;
392static void *fallback_port_data = NULL;
393
394static hash_table_t interface_hash_table;
395
396static size_t interface_key_hash(void *key)
397{
398 iface_t iface = *(iface_t *) key;
399 return iface;
400}
401
402static size_t interface_hash(const ht_link_t *item)
403{
404 interface_t *interface = hash_table_get_inst(item, interface_t, link);
405 return interface_key_hash(&interface->iface);
406}
407
408static bool interface_key_equal(void *key, const ht_link_t *item)
409{
410 iface_t iface = *(iface_t *) key;
411 interface_t *interface = hash_table_get_inst(item, interface_t, link);
412 return iface == interface->iface;
413}
414
415/** Operations for the port hash table. */
416static hash_table_ops_t interface_hash_table_ops = {
417 .hash = interface_hash,
418 .key_hash = interface_key_hash,
419 .key_equal = interface_key_equal,
420 .equal = NULL,
421 .remove_callback = NULL
422};
423
424static size_t port_key_hash(void *key)
425{
426 port_id_t port_id = *(port_id_t *) key;
427 return port_id;
428}
429
430static size_t port_hash(const ht_link_t *item)
431{
432 port_t *port = hash_table_get_inst(item, port_t, link);
433 return port_key_hash(&port->id);
434}
435
436static bool port_key_equal(void *key, const ht_link_t *item)
437{
438 port_id_t port_id = *(port_id_t *) key;
439 port_t *port = hash_table_get_inst(item, port_t, link);
440 return port_id == port->id;
441}
442
443/** Operations for the port hash table. */
444static hash_table_ops_t port_hash_table_ops = {
445 .hash = port_hash,
446 .key_hash = port_key_hash,
447 .key_equal = port_key_equal,
448 .equal = NULL,
449 .remove_callback = NULL
450};
451
452static interface_t *async_new_interface(iface_t iface)
453{
454 interface_t *interface =
455 (interface_t *) malloc(sizeof(interface_t));
456 if (!interface)
457 return NULL;
458
459 bool ret = hash_table_create(&interface->port_hash_table, 0, 0,
460 &port_hash_table_ops);
461 if (!ret) {
462 free(interface);
463 return NULL;
464 }
465
466 interface->iface = iface;
467 futex_initialize(&interface->futex, 1);
468 interface->port_id_avail = 0;
469
470 hash_table_insert(&interface_hash_table, &interface->link);
471
472 return interface;
473}
474
475static port_t *async_new_port(interface_t *interface,
476 async_port_handler_t handler, void *data)
477{
478 port_t *port = (port_t *) malloc(sizeof(port_t));
479 if (!port)
480 return NULL;
481
482 futex_down(&interface->futex);
483
484 port_id_t id = interface->port_id_avail;
485 interface->port_id_avail++;
486
487 port->id = id;
488 port->handler = handler;
489 port->data = data;
490
491 hash_table_insert(&interface->port_hash_table, &port->link);
492
493 futex_up(&interface->futex);
494
495 return port;
496}
497
498/** Mutex protecting inactive_exch_list and avail_phone_cv.
499 *
500 */
501static FIBRIL_MUTEX_INITIALIZE(async_sess_mutex);
502
503/** List of all currently inactive exchanges.
504 *
505 */
506static LIST_INITIALIZE(inactive_exch_list);
507
508/** Condition variable to wait for a phone to become available.
509 *
510 */
511static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
512
513errno_t async_create_port(iface_t iface, async_port_handler_t handler,
514 void *data, port_id_t *port_id)
515{
516 if ((iface & IFACE_MOD_MASK) == IFACE_MOD_CALLBACK)
517 return EINVAL;
518
519 interface_t *interface;
520
521 futex_down(&async_futex);
522
523 ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
524 if (link)
525 interface = hash_table_get_inst(link, interface_t, link);
526 else
527 interface = async_new_interface(iface);
528
529 if (!interface) {
530 futex_up(&async_futex);
531 return ENOMEM;
532 }
533
534 port_t *port = async_new_port(interface, handler, data);
535 if (!port) {
536 futex_up(&async_futex);
537 return ENOMEM;
538 }
539
540 *port_id = port->id;
541
542 futex_up(&async_futex);
543
544 return EOK;
545}
546
547void async_set_fallback_port_handler(async_port_handler_t handler, void *data)
548{
549 assert(handler != NULL);
550
551 fallback_port_handler = handler;
552 fallback_port_data = data;
553}
554
555static hash_table_t client_hash_table;
556static hash_table_t conn_hash_table;
557static hash_table_t notification_hash_table;
558static LIST_INITIALIZE(timeout_list);
559
560static sysarg_t notification_avail = 0;
561
562static size_t client_key_hash(void *key)
563{
564 task_id_t in_task_id = *(task_id_t *) key;
565 return in_task_id;
566}
567
568static size_t client_hash(const ht_link_t *item)
569{
570 client_t *client = hash_table_get_inst(item, client_t, link);
571 return client_key_hash(&client->in_task_id);
572}
573
574static bool client_key_equal(void *key, const ht_link_t *item)
575{
576 task_id_t in_task_id = *(task_id_t *) key;
577 client_t *client = hash_table_get_inst(item, client_t, link);
578 return in_task_id == client->in_task_id;
579}
580
581/** Operations for the client hash table. */
582static hash_table_ops_t client_hash_table_ops = {
583 .hash = client_hash,
584 .key_hash = client_key_hash,
585 .key_equal = client_key_equal,
586 .equal = NULL,
587 .remove_callback = NULL
588};
589
590typedef struct {
591 task_id_t task_id;
592 sysarg_t phone_hash;
593} conn_key_t;
594
595/** Compute hash into the connection hash table
596 *
597 * The hash is based on the source task ID and the source phone hash. The task
598 * ID is included in the hash because a phone hash alone might not be unique
599 * while we still track connections for killed tasks due to kernel's recycling
600 * of phone structures.
601 *
602 * @param key Pointer to the connection key structure.
603 *
604 * @return Index into the connection hash table.
605 *
606 */
607static size_t conn_key_hash(void *key)
608{
609 conn_key_t *ck = (conn_key_t *) key;
610
611 size_t hash = 0;
612 hash = hash_combine(hash, LOWER32(ck->task_id));
613 hash = hash_combine(hash, UPPER32(ck->task_id));
614 hash = hash_combine(hash, ck->phone_hash);
615 return hash;
616}
617
618static size_t conn_hash(const ht_link_t *item)
619{
620 connection_t *conn = hash_table_get_inst(item, connection_t, link);
621 return conn_key_hash(&(conn_key_t){
622 .task_id = conn->in_task_id,
623 .phone_hash = conn->in_phone_hash
624 });
625}
626
627static bool conn_key_equal(void *key, const ht_link_t *item)
628{
629 conn_key_t *ck = (conn_key_t *) key;
630 connection_t *conn = hash_table_get_inst(item, connection_t, link);
631 return ((ck->task_id == conn->in_task_id) &&
632 (ck->phone_hash == conn->in_phone_hash));
633}
634
635/** Operations for the connection hash table. */
636static hash_table_ops_t conn_hash_table_ops = {
637 .hash = conn_hash,
638 .key_hash = conn_key_hash,
639 .key_equal = conn_key_equal,
640 .equal = NULL,
641 .remove_callback = NULL
642};
643
644static client_t *async_client_get(task_id_t client_id, bool create)
645{
646 client_t *client = NULL;
647
648 futex_down(&async_futex);
649 ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
650 if (link) {
651 client = hash_table_get_inst(link, client_t, link);
652 atomic_inc(&client->refcnt);
653 } else if (create) {
654 client = malloc(sizeof(client_t));
655 if (client) {
656 client->in_task_id = client_id;
657 client->data = async_client_data_create();
658
659 atomic_set(&client->refcnt, 1);
660 hash_table_insert(&client_hash_table, &client->link);
661 }
662 }
663
664 futex_up(&async_futex);
665 return client;
666}
667
668static void async_client_put(client_t *client)
669{
670 bool destroy;
671
672 futex_down(&async_futex);
673
674 if (atomic_predec(&client->refcnt) == 0) {
675 hash_table_remove(&client_hash_table, &client->in_task_id);
676 destroy = true;
677 } else
678 destroy = false;
679
680 futex_up(&async_futex);
681
682 if (destroy) {
683 if (client->data)
684 async_client_data_destroy(client->data);
685
686 free(client);
687 }
688}
689
690/** Wrapper for client connection fibril.
691 *
692 * When a new connection arrives, a fibril with this implementing
693 * function is created.
694 *
695 * @param arg Connection structure pointer.
696 *
697 * @return Always zero.
698 *
699 */
700static errno_t connection_fibril(void *arg)
701{
702 assert(arg);
703
704 /*
705 * Setup fibril-local connection pointer.
706 */
707 fibril_connection = (connection_t *) arg;
708
709 /*
710 * Add our reference for the current connection in the client task
711 * tracking structure. If this is the first reference, create and
712 * hash in a new tracking structure.
713 */
714
715 client_t *client = async_client_get(fibril_connection->in_task_id, true);
716 if (!client) {
717 ipc_answer_0(fibril_connection->chandle, ENOMEM);
718 return 0;
719 }
720
721 fibril_connection->client = client;
722
723 /*
724 * Call the connection handler function.
725 */
726 fibril_connection->handler(fibril_connection->chandle,
727 &fibril_connection->call, fibril_connection->data);
728
729 /*
730 * Remove the reference for this client task connection.
731 */
732 async_client_put(client);
733
734 /*
735 * Remove myself from the connection hash table.
736 */
737 futex_down(&async_futex);
738 hash_table_remove(&conn_hash_table, &(conn_key_t){
739 .task_id = fibril_connection->in_task_id,
740 .phone_hash = fibril_connection->in_phone_hash
741 });
742 futex_up(&async_futex);
743
744 /*
745 * Answer all remaining messages with EHANGUP.
746 */
747 while (!list_empty(&fibril_connection->msg_queue)) {
748 msg_t *msg =
749 list_get_instance(list_first(&fibril_connection->msg_queue),
750 msg_t, link);
751
752 list_remove(&msg->link);
753 ipc_answer_0(msg->chandle, EHANGUP);
754 free(msg);
755 }
756
757 /*
758 * If the connection was hung-up, answer the last call,
759 * i.e. IPC_M_PHONE_HUNGUP.
760 */
761 if (fibril_connection->close_chandle)
762 ipc_answer_0(fibril_connection->close_chandle, EOK);
763
764 free(fibril_connection);
765 return EOK;
766}
767
768/** Create a new fibril for a new connection.
769 *
770 * Create new fibril for connection, fill in connection structures and insert it
771 * into the hash table, so that later we can easily do routing of messages to
772 * particular fibrils.
773 *
774 * @param in_task_id Identification of the incoming connection.
775 * @param in_phone_hash Identification of the incoming connection.
776 * @param chandle Handle of the opening IPC_M_CONNECT_ME_TO call.
777 * If chandle is CAP_NIL, the connection was opened by
778 * accepting the IPC_M_CONNECT_TO_ME call and this
779 * function is called directly by the server.
780 * @param call Call data of the opening call.
781 * @param handler Connection handler.
782 * @param data Client argument to pass to the connection handler.
783 *
784 * @return New fibril id or NULL on failure.
785 *
786 */
787static fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
788 cap_call_handle_t chandle, ipc_call_t *call, async_port_handler_t handler,
789 void *data)
790{
791 connection_t *conn = malloc(sizeof(*conn));
792 if (!conn) {
793 if (chandle != CAP_NIL)
794 ipc_answer_0(chandle, ENOMEM);
795
796 return (uintptr_t) NULL;
797 }
798
799 conn->in_task_id = in_task_id;
800 conn->in_phone_hash = in_phone_hash;
801 list_initialize(&conn->msg_queue);
802 conn->chandle = chandle;
803 conn->close_chandle = CAP_NIL;
804 conn->handler = handler;
805 conn->data = data;
806
807 if (call)
808 conn->call = *call;
809
810 /* We will activate the fibril ASAP */
811 conn->wdata.active = true;
812 conn->wdata.fid = fibril_create(connection_fibril, conn);
813
814 if (conn->wdata.fid == 0) {
815 free(conn);
816
817 if (chandle != CAP_NIL)
818 ipc_answer_0(chandle, ENOMEM);
819
820 return (uintptr_t) NULL;
821 }
822
823 /* Add connection to the connection hash table */
824
825 futex_down(&async_futex);
826 hash_table_insert(&conn_hash_table, &conn->link);
827 futex_up(&async_futex);
828
829 fibril_add_ready(conn->wdata.fid);
830
831 return conn->wdata.fid;
832}
833
834/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
835 *
836 * Ask through phone for a new connection to some service.
837 *
838 * @param exch Exchange for sending the message.
839 * @param iface Callback interface.
840 * @param arg1 User defined argument.
841 * @param arg2 User defined argument.
842 * @param handler Callback handler.
843 * @param data Handler data.
844 * @param port_id ID of the newly created port.
845 *
846 * @return Zero on success or an error code.
847 *
848 */
849errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
850 sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
851{
852 if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
853 return EINVAL;
854
855 if (exch == NULL)
856 return ENOENT;
857
858 ipc_call_t answer;
859 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
860 &answer);
861
862 errno_t ret;
863 async_wait_for(req, &ret);
864 if (ret != EOK)
865 return (errno_t) ret;
866
867 sysarg_t phone_hash = IPC_GET_ARG5(answer);
868 interface_t *interface;
869
870 futex_down(&async_futex);
871
872 ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
873 if (link)
874 interface = hash_table_get_inst(link, interface_t, link);
875 else
876 interface = async_new_interface(iface);
877
878 if (!interface) {
879 futex_up(&async_futex);
880 return ENOMEM;
881 }
882
883 port_t *port = async_new_port(interface, handler, data);
884 if (!port) {
885 futex_up(&async_futex);
886 return ENOMEM;
887 }
888
889 *port_id = port->id;
890
891 futex_up(&async_futex);
892
893 fid_t fid = async_new_connection(answer.in_task_id, phone_hash,
894 CAP_NIL, NULL, handler, data);
895 if (fid == (uintptr_t) NULL)
896 return ENOMEM;
897
898 return EOK;
899}
900
901static size_t notification_key_hash(void *key)
902{
903 sysarg_t id = *(sysarg_t *) key;
904 return id;
905}
906
907static size_t notification_hash(const ht_link_t *item)
908{
909 notification_t *notification =
910 hash_table_get_inst(item, notification_t, link);
911 return notification_key_hash(&notification->imethod);
912}
913
914static bool notification_key_equal(void *key, const ht_link_t *item)
915{
916 sysarg_t id = *(sysarg_t *) key;
917 notification_t *notification =
918 hash_table_get_inst(item, notification_t, link);
919 return id == notification->imethod;
920}
921
922/** Operations for the notification hash table. */
923static hash_table_ops_t notification_hash_table_ops = {
924 .hash = notification_hash,
925 .key_hash = notification_key_hash,
926 .key_equal = notification_key_equal,
927 .equal = NULL,
928 .remove_callback = NULL
929};
930
931/** Sort in current fibril's timeout request.
932 *
933 * @param wd Wait data of the current fibril.
934 *
935 */
936void async_insert_timeout(awaiter_t *wd)
937{
938 assert(wd);
939
940 wd->to_event.occurred = false;
941 wd->to_event.inlist = true;
942
943 link_t *tmp = timeout_list.head.next;
944 while (tmp != &timeout_list.head) {
945 awaiter_t *cur
946 = list_get_instance(tmp, awaiter_t, to_event.link);
947
948 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
949 break;
950
951 tmp = tmp->next;
952 }
953
954 list_insert_before(&wd->to_event.link, tmp);
955}
956
957/** Try to route a call to an appropriate connection fibril.
958 *
959 * If the proper connection fibril is found, a message with the call is added to
960 * its message queue. If the fibril was not active, it is activated and all
961 * timeouts are unregistered.
962 *
963 * @param chandle Handle of the incoming call.
964 * @param call Data of the incoming call.
965 *
966 * @return False if the call doesn't match any connection.
967 * @return True if the call was passed to the respective connection fibril.
968 *
969 */
970static bool route_call(cap_call_handle_t chandle, ipc_call_t *call)
971{
972 assert(call);
973
974 futex_down(&async_futex);
975
976 ht_link_t *link = hash_table_find(&conn_hash_table, &(conn_key_t){
977 .task_id = call->in_task_id,
978 .phone_hash = call->in_phone_hash
979 });
980 if (!link) {
981 futex_up(&async_futex);
982 return false;
983 }
984
985 connection_t *conn = hash_table_get_inst(link, connection_t, link);
986
987 msg_t *msg = malloc(sizeof(*msg));
988 if (!msg) {
989 futex_up(&async_futex);
990 return false;
991 }
992
993 msg->chandle = chandle;
994 msg->call = *call;
995 list_append(&msg->link, &conn->msg_queue);
996
997 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
998 conn->close_chandle = chandle;
999
1000 /* If the connection fibril is waiting for an event, activate it */
1001 if (!conn->wdata.active) {
1002
1003 /* If in timeout list, remove it */
1004 if (conn->wdata.to_event.inlist) {
1005 conn->wdata.to_event.inlist = false;
1006 list_remove(&conn->wdata.to_event.link);
1007 }
1008
1009 conn->wdata.active = true;
1010 fibril_add_ready(conn->wdata.fid);
1011 }
1012
1013 futex_up(&async_futex);
1014 return true;
1015}
1016
1017/** Process notification.
1018 *
1019 * @param call Data of the incoming call.
1020 *
1021 */
1022static void process_notification(ipc_call_t *call)
1023{
1024 async_notification_handler_t handler = NULL;
1025 void *data = NULL;
1026
1027 assert(call);
1028
1029 futex_down(&async_futex);
1030
1031 ht_link_t *link = hash_table_find(&notification_hash_table,
1032 &IPC_GET_IMETHOD(*call));
1033 if (link) {
1034 notification_t *notification =
1035 hash_table_get_inst(link, notification_t, link);
1036 handler = notification->handler;
1037 data = notification->data;
1038 }
1039
1040 futex_up(&async_futex);
1041
1042 if (handler)
1043 handler(call, data);
1044}
1045
1046/** Subscribe to IRQ notification.
1047 *
1048 * @param inr IRQ number.
1049 * @param handler Notification handler.
1050 * @param data Notification handler client data.
1051 * @param ucode Top-half pseudocode handler.
1052 *
1053 * @param[out] handle IRQ capability handle on success.
1054 *
1055 * @return An error code.
1056 *
1057 */
1058errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
1059 void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
1060{
1061 notification_t *notification =
1062 (notification_t *) malloc(sizeof(notification_t));
1063 if (!notification)
1064 return ENOMEM;
1065
1066 futex_down(&async_futex);
1067
1068 sysarg_t imethod = notification_avail;
1069 notification_avail++;
1070
1071 notification->imethod = imethod;
1072 notification->handler = handler;
1073 notification->data = data;
1074
1075 hash_table_insert(&notification_hash_table, &notification->link);
1076
1077 futex_up(&async_futex);
1078
1079 cap_irq_handle_t ihandle;
1080 errno_t rc = ipc_irq_subscribe(inr, imethod, ucode, &ihandle);
1081 if (rc == EOK && handle != NULL) {
1082 *handle = ihandle;
1083 }
1084 return rc;
1085}
1086
1087/** Unsubscribe from IRQ notification.
1088 *
1089 * @param handle IRQ capability handle.
1090 *
1091 * @return Zero on success or an error code.
1092 *
1093 */
1094errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
1095{
1096 // TODO: Remove entry from hash table
1097 // to avoid memory leak
1098
1099 return ipc_irq_unsubscribe(ihandle);
1100}
1101
1102/** Subscribe to event notifications.
1103 *
1104 * @param evno Event type to subscribe.
1105 * @param handler Notification handler.
1106 * @param data Notification handler client data.
1107 *
1108 * @return Zero on success or an error code.
1109 *
1110 */
1111errno_t async_event_subscribe(event_type_t evno,
1112 async_notification_handler_t handler, void *data)
1113{
1114 notification_t *notification =
1115 (notification_t *) malloc(sizeof(notification_t));
1116 if (!notification)
1117 return ENOMEM;
1118
1119 futex_down(&async_futex);
1120
1121 sysarg_t imethod = notification_avail;
1122 notification_avail++;
1123
1124 notification->imethod = imethod;
1125 notification->handler = handler;
1126 notification->data = data;
1127
1128 hash_table_insert(&notification_hash_table, &notification->link);
1129
1130 futex_up(&async_futex);
1131
1132 return ipc_event_subscribe(evno, imethod);
1133}
1134
1135/** Subscribe to task event notifications.
1136 *
1137 * @param evno Event type to subscribe.
1138 * @param handler Notification handler.
1139 * @param data Notification handler client data.
1140 *
1141 * @return Zero on success or an error code.
1142 *
1143 */
1144errno_t async_event_task_subscribe(event_task_type_t evno,
1145 async_notification_handler_t handler, void *data)
1146{
1147 notification_t *notification =
1148 (notification_t *) malloc(sizeof(notification_t));
1149 if (!notification)
1150 return ENOMEM;
1151
1152 futex_down(&async_futex);
1153
1154 sysarg_t imethod = notification_avail;
1155 notification_avail++;
1156
1157 notification->imethod = imethod;
1158 notification->handler = handler;
1159 notification->data = data;
1160
1161 hash_table_insert(&notification_hash_table, &notification->link);
1162
1163 futex_up(&async_futex);
1164
1165 return ipc_event_task_subscribe(evno, imethod);
1166}
1167
1168/** Unmask event notifications.
1169 *
1170 * @param evno Event type to unmask.
1171 *
1172 * @return Value returned by the kernel.
1173 *
1174 */
1175errno_t async_event_unmask(event_type_t evno)
1176{
1177 return ipc_event_unmask(evno);
1178}
1179
1180/** Unmask task event notifications.
1181 *
1182 * @param evno Event type to unmask.
1183 *
1184 * @return Value returned by the kernel.
1185 *
1186 */
1187errno_t async_event_task_unmask(event_task_type_t evno)
1188{
1189 return ipc_event_task_unmask(evno);
1190}
1191
1192/** Return new incoming message for the current (fibril-local) connection.
1193 *
1194 * @param call Storage where the incoming call data will be stored.
1195 * @param usecs Timeout in microseconds. Zero denotes no timeout.
1196 *
1197 * @return If no timeout was specified, then a handle of the incoming call is
1198 * returned. If a timeout is specified, then a handle of the incoming
1199 * call is returned unless the timeout expires prior to receiving a
1200 * message. In that case zero CAP_NIL is returned.
1201 */
1202cap_call_handle_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
1203{
1204 assert(call);
1205 assert(fibril_connection);
1206
1207 /* Why doing this?
1208 * GCC 4.1.0 coughs on fibril_connection-> dereference.
1209 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
1210 * I would never expect to find so many errors in
1211 * a compiler.
1212 */
1213 connection_t *conn = fibril_connection;
1214
1215 futex_down(&async_futex);
1216
1217 if (usecs) {
1218 getuptime(&conn->wdata.to_event.expires);
1219 tv_add_diff(&conn->wdata.to_event.expires, usecs);
1220 } else
1221 conn->wdata.to_event.inlist = false;
1222
1223 /* If nothing in queue, wait until something arrives */
1224 while (list_empty(&conn->msg_queue)) {
1225 if (conn->close_chandle) {
1226 /*
1227 * Handle the case when the connection was already
1228 * closed by the client but the server did not notice
1229 * the first IPC_M_PHONE_HUNGUP call and continues to
1230 * call async_get_call_timeout(). Repeat
1231 * IPC_M_PHONE_HUNGUP until the caller notices.
1232 */
1233 memset(call, 0, sizeof(ipc_call_t));
1234 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
1235 futex_up(&async_futex);
1236 return conn->close_chandle;
1237 }
1238
1239 if (usecs)
1240 async_insert_timeout(&conn->wdata);
1241
1242 conn->wdata.active = false;
1243
1244 /*
1245 * Note: the current fibril will be rescheduled either due to a
1246 * timeout or due to an arriving message destined to it. In the
1247 * former case, handle_expired_timeouts() and, in the latter
1248 * case, route_call() will perform the wakeup.
1249 */
1250 fibril_switch(FIBRIL_TO_MANAGER);
1251
1252 /*
1253 * Futex is up after getting back from async_manager.
1254 * Get it again.
1255 */
1256 futex_down(&async_futex);
1257 if ((usecs) && (conn->wdata.to_event.occurred)
1258 && (list_empty(&conn->msg_queue))) {
1259 /* If we timed out -> exit */
1260 futex_up(&async_futex);
1261 return CAP_NIL;
1262 }
1263 }
1264
1265 msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
1266 msg_t, link);
1267 list_remove(&msg->link);
1268
1269 cap_call_handle_t chandle = msg->chandle;
1270 *call = msg->call;
1271 free(msg);
1272
1273 futex_up(&async_futex);
1274 return chandle;
1275}
1276
1277void *async_get_client_data(void)
1278{
1279 assert(fibril_connection);
1280 return fibril_connection->client->data;
1281}
1282
1283void *async_get_client_data_by_id(task_id_t client_id)
1284{
1285 client_t *client = async_client_get(client_id, false);
1286 if (!client)
1287 return NULL;
1288
1289 if (!client->data) {
1290 async_client_put(client);
1291 return NULL;
1292 }
1293
1294 return client->data;
1295}
1296
1297void async_put_client_data_by_id(task_id_t client_id)
1298{
1299 client_t *client = async_client_get(client_id, false);
1300
1301 assert(client);
1302 assert(client->data);
1303
1304 /* Drop the reference we got in async_get_client_data_by_hash(). */
1305 async_client_put(client);
1306
1307 /* Drop our own reference we got at the beginning of this function. */
1308 async_client_put(client);
1309}
1310
1311static port_t *async_find_port(iface_t iface, port_id_t port_id)
1312{
1313 port_t *port = NULL;
1314
1315 futex_down(&async_futex);
1316
1317 ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
1318 if (link) {
1319 interface_t *interface =
1320 hash_table_get_inst(link, interface_t, link);
1321
1322 link = hash_table_find(&interface->port_hash_table, &port_id);
1323 if (link)
1324 port = hash_table_get_inst(link, port_t, link);
1325 }
1326
1327 futex_up(&async_futex);
1328
1329 return port;
1330}
1331
1332/** Handle a call that was received.
1333 *
1334 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
1335 * Otherwise the call is routed to its connection fibril.
1336 *
1337 * @param chandle Handle of the incoming call.
1338 * @param call Data of the incoming call.
1339 *
1340 */
1341static void handle_call(cap_call_handle_t chandle, ipc_call_t *call)
1342{
1343 assert(call);
1344
1345 /* Kernel notification */
1346 if ((chandle == CAP_NIL) && (call->flags & IPC_CALL_NOTIF)) {
1347 fibril_t *fibril = (fibril_t *) __tcb_get()->fibril_data;
1348 unsigned oldsw = fibril->switches;
1349
1350 process_notification(call);
1351
1352 if (oldsw != fibril->switches) {
1353 /*
1354 * The notification handler did not execute atomically
1355 * and so the current manager fibril assumed the role of
1356 * a notification fibril. While waiting for its
1357 * resources, it switched to another manager fibril that
1358 * had already existed or it created a new one. We
1359 * therefore know there is at least yet another
1360 * manager fibril that can take over. We now kill the
1361 * current 'notification' fibril to prevent fibril
1362 * population explosion.
1363 */
1364 futex_down(&async_futex);
1365 fibril_switch(FIBRIL_FROM_DEAD);
1366 }
1367
1368 return;
1369 }
1370
1371 /* New connection */
1372 if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
1373 iface_t iface = (iface_t) IPC_GET_ARG1(*call);
1374 sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
1375
1376 async_port_handler_t handler = fallback_port_handler;
1377 void *data = fallback_port_data;
1378
1379 // TODO: Currently ignores all ports but the first one
1380 port_t *port = async_find_port(iface, 0);
1381 if (port) {
1382 handler = port->handler;
1383 data = port->data;
1384 }
1385
1386 async_new_connection(call->in_task_id, in_phone_hash, chandle,
1387 call, handler, data);
1388 return;
1389 }
1390
1391 /* Try to route the call through the connection hash table */
1392 if (route_call(chandle, call))
1393 return;
1394
1395 /* Unknown call from unknown phone - hang it up */
1396 ipc_answer_0(chandle, EHANGUP);
1397}
1398
1399/** Fire all timeouts that expired. */
1400static void handle_expired_timeouts(void)
1401{
1402 struct timeval tv;
1403 getuptime(&tv);
1404
1405 futex_down(&async_futex);
1406
1407 link_t *cur = list_first(&timeout_list);
1408 while (cur != NULL) {
1409 awaiter_t *waiter =
1410 list_get_instance(cur, awaiter_t, to_event.link);
1411
1412 if (tv_gt(&waiter->to_event.expires, &tv))
1413 break;
1414
1415 list_remove(&waiter->to_event.link);
1416 waiter->to_event.inlist = false;
1417 waiter->to_event.occurred = true;
1418
1419 /*
1420 * Redundant condition?
1421 * The fibril should not be active when it gets here.
1422 */
1423 if (!waiter->active) {
1424 waiter->active = true;
1425 fibril_add_ready(waiter->fid);
1426 }
1427
1428 cur = list_first(&timeout_list);
1429 }
1430
1431 futex_up(&async_futex);
1432}
1433
1434/** Endless loop dispatching incoming calls and answers.
1435 *
1436 * @return Never returns.
1437 *
1438 */
1439static errno_t async_manager_worker(void)
1440{
1441 while (true) {
1442 if (fibril_switch(FIBRIL_FROM_MANAGER)) {
1443 futex_up(&async_futex);
1444 /*
1445 * async_futex is always held when entering a manager
1446 * fibril.
1447 */
1448 continue;
1449 }
1450
1451 futex_down(&async_futex);
1452
1453 suseconds_t timeout;
1454 unsigned int flags = SYNCH_FLAGS_NONE;
1455 if (!list_empty(&timeout_list)) {
1456 awaiter_t *waiter = list_get_instance(
1457 list_first(&timeout_list), awaiter_t, to_event.link);
1458
1459 struct timeval tv;
1460 getuptime(&tv);
1461
1462 if (tv_gteq(&tv, &waiter->to_event.expires)) {
1463 futex_up(&async_futex);
1464 handle_expired_timeouts();
1465 /*
1466 * Notice that even if the event(s) already
1467 * expired (and thus the other fibril was
1468 * supposed to be running already),
1469 * we check for incoming IPC.
1470 *
1471 * Otherwise, a fibril that continuously
1472 * creates (almost) expired events could
1473 * prevent IPC retrieval from the kernel.
1474 */
1475 timeout = 0;
1476 flags = SYNCH_FLAGS_NON_BLOCKING;
1477
1478 } else {
1479 timeout = tv_sub_diff(&waiter->to_event.expires,
1480 &tv);
1481 futex_up(&async_futex);
1482 }
1483 } else {
1484 futex_up(&async_futex);
1485 timeout = SYNCH_NO_TIMEOUT;
1486 }
1487
1488 atomic_inc(&threads_in_ipc_wait);
1489
1490 ipc_call_t call;
1491 errno_t rc = ipc_wait_cycle(&call, timeout, flags);
1492
1493 atomic_dec(&threads_in_ipc_wait);
1494
1495 assert(rc == EOK);
1496
1497 if (call.cap_handle == CAP_NIL) {
1498 if ((call.flags &
1499 (IPC_CALL_NOTIF | IPC_CALL_ANSWERED)) == 0) {
1500 /* Neither a notification nor an answer. */
1501 handle_expired_timeouts();
1502 continue;
1503 }
1504 }
1505
1506 if (call.flags & IPC_CALL_ANSWERED)
1507 continue;
1508
1509 handle_call(call.cap_handle, &call);
1510 }
1511
1512 return 0;
1513}
1514
1515/** Function to start async_manager as a standalone fibril.
1516 *
1517 * When more kernel threads are used, one async manager should exist per thread.
1518 *
1519 * @param arg Unused.
1520 * @return Never returns.
1521 *
1522 */
1523static errno_t async_manager_fibril(void *arg)
1524{
1525 futex_up(&async_futex);
1526
1527 /*
1528 * async_futex is always locked when entering manager
1529 */
1530 async_manager_worker();
1531
1532 return 0;
1533}
1534
1535/** Add one manager to manager list. */
1536void async_create_manager(void)
1537{
1538 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
1539 if (fid != 0)
1540 fibril_add_manager(fid);
1541}
1542
1543/** Remove one manager from manager list */
1544void async_destroy_manager(void)
1545{
1546 fibril_remove_manager();
1547}
1548
1549/** Initialize the async framework.
1550 *
1551 */
1552void __async_init(void)
1553{
1554 if (!hash_table_create(&interface_hash_table, 0, 0,
1555 &interface_hash_table_ops))
1556 abort();
1557
1558 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
1559 abort();
1560
1561 if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
1562 abort();
1563
1564 if (!hash_table_create(&notification_hash_table, 0, 0,
1565 &notification_hash_table_ops))
1566 abort();
1567
1568 session_ns = (async_sess_t *) malloc(sizeof(async_sess_t));
1569 if (session_ns == NULL)
1570 abort();
1571
1572 session_ns->iface = 0;
1573 session_ns->mgmt = EXCHANGE_ATOMIC;
1574 session_ns->phone = PHONE_NS;
1575 session_ns->arg1 = 0;
1576 session_ns->arg2 = 0;
1577 session_ns->arg3 = 0;
1578
1579 fibril_mutex_initialize(&session_ns->remote_state_mtx);
1580 session_ns->remote_state_data = NULL;
1581
1582 list_initialize(&session_ns->exch_list);
1583 fibril_mutex_initialize(&session_ns->mutex);
1584 atomic_set(&session_ns->refcnt, 0);
1585}
1586
1587/** Reply received callback.
1588 *
1589 * This function is called whenever a reply for an asynchronous message sent out
1590 * by the asynchronous framework is received.
1591 *
1592 * Notify the fibril which is waiting for this message that it has arrived.
1593 *
1594 * @param arg Pointer to the asynchronous message record.
1595 * @param retval Value returned in the answer.
1596 * @param data Call data of the answer.
1597 *
1598 */
1599void reply_received(void *arg, errno_t retval, ipc_call_t *data)
1600{
1601 assert(arg);
1602
1603 futex_down(&async_futex);
1604
1605 amsg_t *msg = (amsg_t *) arg;
1606 msg->retval = retval;
1607
1608 /* Copy data after futex_down, just in case the call was detached */
1609 if ((msg->dataptr) && (data))
1610 *msg->dataptr = *data;
1611
1612 write_barrier();
1613
1614 /* Remove message from timeout list */
1615 if (msg->wdata.to_event.inlist)
1616 list_remove(&msg->wdata.to_event.link);
1617
1618 msg->done = true;
1619
1620 if (msg->forget) {
1621 assert(msg->wdata.active);
1622 amsg_destroy(msg);
1623 } else if (!msg->wdata.active) {
1624 msg->wdata.active = true;
1625 fibril_add_ready(msg->wdata.fid);
1626 }
1627
1628 futex_up(&async_futex);
1629}
1630
1631/** Send message and return id of the sent message.
1632 *
1633 * The return value can be used as input for async_wait() to wait for
1634 * completion.
1635 *
1636 * @param exch Exchange for sending the message.
1637 * @param imethod Service-defined interface and method.
1638 * @param arg1 Service-defined payload argument.
1639 * @param arg2 Service-defined payload argument.
1640 * @param arg3 Service-defined payload argument.
1641 * @param arg4 Service-defined payload argument.
1642 * @param dataptr If non-NULL, storage where the reply data will be stored.
1643 *
1644 * @return Hash of the sent message or 0 on error.
1645 *
1646 */
1647aid_t async_send_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1648 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
1649{
1650 if (exch == NULL)
1651 return 0;
1652
1653 amsg_t *msg = amsg_create();
1654 if (msg == NULL)
1655 return 0;
1656
1657 msg->dataptr = dataptr;
1658 msg->wdata.active = true;
1659
1660 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4, msg,
1661 reply_received);
1662
1663 return (aid_t) msg;
1664}
1665
1666/** Send message and return id of the sent message
1667 *
1668 * The return value can be used as input for async_wait() to wait for
1669 * completion.
1670 *
1671 * @param exch Exchange for sending the message.
1672 * @param imethod Service-defined interface and method.
1673 * @param arg1 Service-defined payload argument.
1674 * @param arg2 Service-defined payload argument.
1675 * @param arg3 Service-defined payload argument.
1676 * @param arg4 Service-defined payload argument.
1677 * @param arg5 Service-defined payload argument.
1678 * @param dataptr If non-NULL, storage where the reply data will be
1679 * stored.
1680 *
1681 * @return Hash of the sent message or 0 on error.
1682 *
1683 */
1684aid_t async_send_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1685 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
1686 ipc_call_t *dataptr)
1687{
1688 if (exch == NULL)
1689 return 0;
1690
1691 amsg_t *msg = amsg_create();
1692 if (msg == NULL)
1693 return 0;
1694
1695 msg->dataptr = dataptr;
1696 msg->wdata.active = true;
1697
1698 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4, arg5,
1699 msg, reply_received);
1700
1701 return (aid_t) msg;
1702}
1703
1704/** Wait for a message sent by the async framework.
1705 *
1706 * @param amsgid Hash of the message to wait for.
1707 * @param retval Pointer to storage where the retval of the answer will
1708 * be stored.
1709 *
1710 */
1711void async_wait_for(aid_t amsgid, errno_t *retval)
1712{
1713 assert(amsgid);
1714
1715 amsg_t *msg = (amsg_t *) amsgid;
1716
1717 futex_down(&async_futex);
1718
1719 assert(!msg->forget);
1720 assert(!msg->destroyed);
1721
1722 if (msg->done) {
1723 futex_up(&async_futex);
1724 goto done;
1725 }
1726
1727 msg->wdata.fid = fibril_get_id();
1728 msg->wdata.active = false;
1729 msg->wdata.to_event.inlist = false;
1730
1731 /* Leave the async_futex locked when entering this function */
1732 fibril_switch(FIBRIL_TO_MANAGER);
1733
1734 /* Futex is up automatically after fibril_switch */
1735
1736done:
1737 if (retval)
1738 *retval = msg->retval;
1739
1740 amsg_destroy(msg);
1741}
1742
1743/** Wait for a message sent by the async framework, timeout variant.
1744 *
1745 * If the wait times out, the caller may choose to either wait again by calling
1746 * async_wait_for() or async_wait_timeout(), or forget the message via
1747 * async_forget().
1748 *
1749 * @param amsgid Hash of the message to wait for.
1750 * @param retval Pointer to storage where the retval of the answer will
1751 * be stored.
1752 * @param timeout Timeout in microseconds.
1753 *
1754 * @return Zero on success, ETIMEOUT if the timeout has expired.
1755 *
1756 */
1757errno_t async_wait_timeout(aid_t amsgid, errno_t *retval, suseconds_t timeout)
1758{
1759 assert(amsgid);
1760
1761 amsg_t *msg = (amsg_t *) amsgid;
1762
1763 futex_down(&async_futex);
1764
1765 assert(!msg->forget);
1766 assert(!msg->destroyed);
1767
1768 if (msg->done) {
1769 futex_up(&async_futex);
1770 goto done;
1771 }
1772
1773 /*
1774 * Negative timeout is converted to zero timeout to avoid
1775 * using tv_add with negative augmenter.
1776 */
1777 if (timeout < 0)
1778 timeout = 0;
1779
1780 getuptime(&msg->wdata.to_event.expires);
1781 tv_add_diff(&msg->wdata.to_event.expires, timeout);
1782
1783 /*
1784 * Current fibril is inserted as waiting regardless of the
1785 * "size" of the timeout.
1786 *
1787 * Checking for msg->done and immediately bailing out when
1788 * timeout == 0 would mean that the manager fibril would never
1789 * run (consider single threaded program).
1790 * Thus the IPC answer would be never retrieved from the kernel.
1791 *
1792 * Notice that the actual delay would be very small because we
1793 * - switch to manager fibril
1794 * - the manager sees expired timeout
1795 * - and thus adds us back to ready queue
1796 * - manager switches back to some ready fibril
1797 * (prior it, it checks for incoming IPC).
1798 *
1799 */
1800 msg->wdata.fid = fibril_get_id();
1801 msg->wdata.active = false;
1802 async_insert_timeout(&msg->wdata);
1803
1804 /* Leave the async_futex locked when entering this function */
1805 fibril_switch(FIBRIL_TO_MANAGER);
1806
1807 /* Futex is up automatically after fibril_switch */
1808
1809 if (!msg->done)
1810 return ETIMEOUT;
1811
1812done:
1813 if (retval)
1814 *retval = msg->retval;
1815
1816 amsg_destroy(msg);
1817
1818 return 0;
1819}
1820
1821/** Discard the message / reply on arrival.
1822 *
1823 * The message will be marked to be discarded once the reply arrives in
1824 * reply_received(). It is not allowed to call async_wait_for() or
1825 * async_wait_timeout() on this message after a call to this function.
1826 *
1827 * @param amsgid Hash of the message to forget.
1828 */
1829void async_forget(aid_t amsgid)
1830{
1831 amsg_t *msg = (amsg_t *) amsgid;
1832
1833 assert(msg);
1834 assert(!msg->forget);
1835 assert(!msg->destroyed);
1836
1837 futex_down(&async_futex);
1838
1839 if (msg->done) {
1840 amsg_destroy(msg);
1841 } else {
1842 msg->dataptr = NULL;
1843 msg->forget = true;
1844 }
1845
1846 futex_up(&async_futex);
1847}
1848
1849/** Wait for specified time.
1850 *
1851 * The current fibril is suspended but the thread continues to execute.
1852 *
1853 * @param timeout Duration of the wait in microseconds.
1854 *
1855 */
1856void async_usleep(suseconds_t timeout)
1857{
1858 awaiter_t awaiter;
1859 awaiter_initialize(&awaiter);
1860
1861 awaiter.fid = fibril_get_id();
1862
1863 getuptime(&awaiter.to_event.expires);
1864 tv_add_diff(&awaiter.to_event.expires, timeout);
1865
1866 futex_down(&async_futex);
1867
1868 async_insert_timeout(&awaiter);
1869
1870 /* Leave the async_futex locked when entering this function */
1871 fibril_switch(FIBRIL_TO_MANAGER);
1872
1873 /* Futex is up automatically after fibril_switch() */
1874}
1875
1876/** Delay execution for the specified number of seconds
1877 *
1878 * @param sec Number of seconds to sleep
1879 */
1880void async_sleep(unsigned int sec)
1881{
1882 /*
1883 * Sleep in 1000 second steps to support
1884 * full argument range
1885 */
1886
1887 while (sec > 0) {
1888 unsigned int period = (sec > 1000) ? 1000 : sec;
1889
1890 async_usleep(period * 1000000);
1891 sec -= period;
1892 }
1893}
1894
1895/** Pseudo-synchronous message sending - fast version.
1896 *
1897 * Send message asynchronously and return only after the reply arrives.
1898 *
1899 * This function can only transfer 4 register payload arguments. For
1900 * transferring more arguments, see the slower async_req_slow().
1901 *
1902 * @param exch Exchange for sending the message.
1903 * @param imethod Interface and method of the call.
1904 * @param arg1 Service-defined payload argument.
1905 * @param arg2 Service-defined payload argument.
1906 * @param arg3 Service-defined payload argument.
1907 * @param arg4 Service-defined payload argument.
1908 * @param r1 If non-NULL, storage for the 1st reply argument.
1909 * @param r2 If non-NULL, storage for the 2nd reply argument.
1910 * @param r3 If non-NULL, storage for the 3rd reply argument.
1911 * @param r4 If non-NULL, storage for the 4th reply argument.
1912 * @param r5 If non-NULL, storage for the 5th reply argument.
1913 *
1914 * @return Return code of the reply or an error code.
1915 *
1916 */
1917errno_t async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1918 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
1919 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
1920{
1921 if (exch == NULL)
1922 return ENOENT;
1923
1924 ipc_call_t result;
1925 aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
1926 &result);
1927
1928 errno_t rc;
1929 async_wait_for(aid, &rc);
1930
1931 if (r1)
1932 *r1 = IPC_GET_ARG1(result);
1933
1934 if (r2)
1935 *r2 = IPC_GET_ARG2(result);
1936
1937 if (r3)
1938 *r3 = IPC_GET_ARG3(result);
1939
1940 if (r4)
1941 *r4 = IPC_GET_ARG4(result);
1942
1943 if (r5)
1944 *r5 = IPC_GET_ARG5(result);
1945
1946 return rc;
1947}
1948
1949/** Pseudo-synchronous message sending - slow version.
1950 *
1951 * Send message asynchronously and return only after the reply arrives.
1952 *
1953 * @param exch Exchange for sending the message.
1954 * @param imethod Interface and method of the call.
1955 * @param arg1 Service-defined payload argument.
1956 * @param arg2 Service-defined payload argument.
1957 * @param arg3 Service-defined payload argument.
1958 * @param arg4 Service-defined payload argument.
1959 * @param arg5 Service-defined payload argument.
1960 * @param r1 If non-NULL, storage for the 1st reply argument.
1961 * @param r2 If non-NULL, storage for the 2nd reply argument.
1962 * @param r3 If non-NULL, storage for the 3rd reply argument.
1963 * @param r4 If non-NULL, storage for the 4th reply argument.
1964 * @param r5 If non-NULL, storage for the 5th reply argument.
1965 *
1966 * @return Return code of the reply or an error code.
1967 *
1968 */
1969errno_t async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1970 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
1971 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
1972{
1973 if (exch == NULL)
1974 return ENOENT;
1975
1976 ipc_call_t result;
1977 aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
1978 &result);
1979
1980 errno_t rc;
1981 async_wait_for(aid, &rc);
1982
1983 if (r1)
1984 *r1 = IPC_GET_ARG1(result);
1985
1986 if (r2)
1987 *r2 = IPC_GET_ARG2(result);
1988
1989 if (r3)
1990 *r3 = IPC_GET_ARG3(result);
1991
1992 if (r4)
1993 *r4 = IPC_GET_ARG4(result);
1994
1995 if (r5)
1996 *r5 = IPC_GET_ARG5(result);
1997
1998 return rc;
1999}
2000
2001void async_msg_0(async_exch_t *exch, sysarg_t imethod)
2002{
2003 if (exch != NULL)
2004 ipc_call_async_0(exch->phone, imethod, NULL, NULL);
2005}
2006
2007void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
2008{
2009 if (exch != NULL)
2010 ipc_call_async_1(exch->phone, imethod, arg1, NULL, NULL);
2011}
2012
2013void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2014 sysarg_t arg2)
2015{
2016 if (exch != NULL)
2017 ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL, NULL);
2018}
2019
2020void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2021 sysarg_t arg2, sysarg_t arg3)
2022{
2023 if (exch != NULL)
2024 ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL,
2025 NULL);
2026}
2027
2028void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2029 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
2030{
2031 if (exch != NULL)
2032 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
2033 NULL, NULL);
2034}
2035
2036void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2037 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
2038{
2039 if (exch != NULL)
2040 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
2041 arg5, NULL, NULL);
2042}
2043
2044errno_t async_answer_0(cap_call_handle_t chandle, errno_t retval)
2045{
2046 return ipc_answer_0(chandle, retval);
2047}
2048
2049errno_t async_answer_1(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1)
2050{
2051 return ipc_answer_1(chandle, retval, arg1);
2052}
2053
2054errno_t async_answer_2(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
2055 sysarg_t arg2)
2056{
2057 return ipc_answer_2(chandle, retval, arg1, arg2);
2058}
2059
2060errno_t async_answer_3(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
2061 sysarg_t arg2, sysarg_t arg3)
2062{
2063 return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
2064}
2065
2066errno_t async_answer_4(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
2067 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
2068{
2069 return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
2070}
2071
2072errno_t async_answer_5(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
2073 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
2074{
2075 return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
2076}
2077
2078errno_t async_forward_fast(cap_call_handle_t chandle, async_exch_t *exch,
2079 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
2080{
2081 if (exch == NULL)
2082 return ENOENT;
2083
2084 return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2, mode);
2085}
2086
2087errno_t async_forward_slow(cap_call_handle_t chandle, async_exch_t *exch,
2088 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
2089 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
2090{
2091 if (exch == NULL)
2092 return ENOENT;
2093
2094 return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
2095 arg4, arg5, mode);
2096}
2097
2098/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
2099 *
2100 * Ask through phone for a new connection to some service.
2101 *
2102 * @param exch Exchange for sending the message.
2103 * @param arg1 User defined argument.
2104 * @param arg2 User defined argument.
2105 * @param arg3 User defined argument.
2106 *
2107 * @return Zero on success or an error code.
2108 *
2109 */
2110errno_t async_connect_to_me(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
2111 sysarg_t arg3)
2112{
2113 if (exch == NULL)
2114 return ENOENT;
2115
2116 ipc_call_t answer;
2117 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
2118 &answer);
2119
2120 errno_t rc;
2121 async_wait_for(req, &rc);
2122 if (rc != EOK)
2123 return (errno_t) rc;
2124
2125 return EOK;
2126}
2127
2128static errno_t async_connect_me_to_internal(cap_phone_handle_t phone,
2129 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
2130 cap_phone_handle_t *out_phone)
2131{
2132 ipc_call_t result;
2133
2134 // XXX: Workaround for GCC's inability to infer association between
2135 // rc == EOK and *out_phone being assigned.
2136 *out_phone = CAP_NIL;
2137
2138 amsg_t *msg = amsg_create();
2139 if (!msg)
2140 return ENOENT;
2141
2142 msg->dataptr = &result;
2143 msg->wdata.active = true;
2144
2145 ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, arg4,
2146 msg, reply_received);
2147
2148 errno_t rc;
2149 async_wait_for((aid_t) msg, &rc);
2150
2151 if (rc != EOK)
2152 return rc;
2153
2154 *out_phone = (cap_phone_handle_t) IPC_GET_ARG5(result);
2155 return EOK;
2156}
2157
2158/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2159 *
2160 * Ask through for a new connection to some service.
2161 *
2162 * @param mgmt Exchange management style.
2163 * @param exch Exchange for sending the message.
2164 * @param arg1 User defined argument.
2165 * @param arg2 User defined argument.
2166 * @param arg3 User defined argument.
2167 *
2168 * @return New session on success or NULL on error.
2169 *
2170 */
2171async_sess_t *async_connect_me_to(exch_mgmt_t mgmt, async_exch_t *exch,
2172 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
2173{
2174 if (exch == NULL) {
2175 errno = ENOENT;
2176 return NULL;
2177 }
2178
2179 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2180 if (sess == NULL) {
2181 errno = ENOMEM;
2182 return NULL;
2183 }
2184
2185 cap_phone_handle_t phone;
2186 errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
2187 0, &phone);
2188 if (rc != EOK) {
2189 errno = rc;
2190 free(sess);
2191 return NULL;
2192 }
2193
2194 sess->iface = 0;
2195 sess->mgmt = mgmt;
2196 sess->phone = phone;
2197 sess->arg1 = arg1;
2198 sess->arg2 = arg2;
2199 sess->arg3 = arg3;
2200
2201 fibril_mutex_initialize(&sess->remote_state_mtx);
2202 sess->remote_state_data = NULL;
2203
2204 list_initialize(&sess->exch_list);
2205 fibril_mutex_initialize(&sess->mutex);
2206 atomic_set(&sess->refcnt, 0);
2207
2208 return sess;
2209}
2210
2211/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2212 *
2213 * Ask through phone for a new connection to some service and block until
2214 * success.
2215 *
2216 * @param exch Exchange for sending the message.
2217 * @param iface Connection interface.
2218 * @param arg2 User defined argument.
2219 * @param arg3 User defined argument.
2220 *
2221 * @return New session on success or NULL on error.
2222 *
2223 */
2224async_sess_t *async_connect_me_to_iface(async_exch_t *exch, iface_t iface,
2225 sysarg_t arg2, sysarg_t arg3)
2226{
2227 if (exch == NULL) {
2228 errno = ENOENT;
2229 return NULL;
2230 }
2231
2232 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2233 if (sess == NULL) {
2234 errno = ENOMEM;
2235 return NULL;
2236 }
2237
2238 cap_phone_handle_t phone;
2239 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
2240 arg3, 0, &phone);
2241 if (rc != EOK) {
2242 errno = rc;
2243 free(sess);
2244 return NULL;
2245 }
2246
2247 sess->iface = iface;
2248 sess->phone = phone;
2249 sess->arg1 = iface;
2250 sess->arg2 = arg2;
2251 sess->arg3 = arg3;
2252
2253 fibril_mutex_initialize(&sess->remote_state_mtx);
2254 sess->remote_state_data = NULL;
2255
2256 list_initialize(&sess->exch_list);
2257 fibril_mutex_initialize(&sess->mutex);
2258 atomic_set(&sess->refcnt, 0);
2259
2260 return sess;
2261}
2262
2263/** Set arguments for new connections.
2264 *
2265 * FIXME This is an ugly hack to work around the problem that parallel
2266 * exchanges are implemented using parallel connections. When we create
2267 * a callback session, the framework does not know arguments for the new
2268 * connections.
2269 *
2270 * The proper solution seems to be to implement parallel exchanges using
2271 * tagging.
2272 */
2273void async_sess_args_set(async_sess_t *sess, sysarg_t arg1, sysarg_t arg2,
2274 sysarg_t arg3)
2275{
2276 sess->arg1 = arg1;
2277 sess->arg2 = arg2;
2278 sess->arg3 = arg3;
2279}
2280
2281/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2282 *
2283 * Ask through phone for a new connection to some service and block until
2284 * success.
2285 *
2286 * @param mgmt Exchange management style.
2287 * @param exch Exchange for sending the message.
2288 * @param arg1 User defined argument.
2289 * @param arg2 User defined argument.
2290 * @param arg3 User defined argument.
2291 *
2292 * @return New session on success or NULL on error.
2293 *
2294 */
2295async_sess_t *async_connect_me_to_blocking(exch_mgmt_t mgmt, async_exch_t *exch,
2296 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
2297{
2298 if (exch == NULL) {
2299 errno = ENOENT;
2300 return NULL;
2301 }
2302
2303 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2304 if (sess == NULL) {
2305 errno = ENOMEM;
2306 return NULL;
2307 }
2308
2309 cap_phone_handle_t phone;
2310 errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
2311 IPC_FLAG_BLOCKING, &phone);
2312
2313 if (rc != EOK) {
2314 errno = rc;
2315 free(sess);
2316 return NULL;
2317 }
2318
2319 sess->iface = 0;
2320 sess->mgmt = mgmt;
2321 sess->phone = phone;
2322 sess->arg1 = arg1;
2323 sess->arg2 = arg2;
2324 sess->arg3 = arg3;
2325
2326 fibril_mutex_initialize(&sess->remote_state_mtx);
2327 sess->remote_state_data = NULL;
2328
2329 list_initialize(&sess->exch_list);
2330 fibril_mutex_initialize(&sess->mutex);
2331 atomic_set(&sess->refcnt, 0);
2332
2333 return sess;
2334}
2335
2336/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2337 *
2338 * Ask through phone for a new connection to some service and block until
2339 * success.
2340 *
2341 * @param exch Exchange for sending the message.
2342 * @param iface Connection interface.
2343 * @param arg2 User defined argument.
2344 * @param arg3 User defined argument.
2345 *
2346 * @return New session on success or NULL on error.
2347 *
2348 */
2349async_sess_t *async_connect_me_to_blocking_iface(async_exch_t *exch, iface_t iface,
2350 sysarg_t arg2, sysarg_t arg3)
2351{
2352 if (exch == NULL) {
2353 errno = ENOENT;
2354 return NULL;
2355 }
2356
2357 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2358 if (sess == NULL) {
2359 errno = ENOMEM;
2360 return NULL;
2361 }
2362
2363 cap_phone_handle_t phone;
2364 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
2365 arg3, IPC_FLAG_BLOCKING, &phone);
2366 if (rc != EOK) {
2367 errno = rc;
2368 free(sess);
2369 return NULL;
2370 }
2371
2372 sess->iface = iface;
2373 sess->phone = phone;
2374 sess->arg1 = iface;
2375 sess->arg2 = arg2;
2376 sess->arg3 = arg3;
2377
2378 fibril_mutex_initialize(&sess->remote_state_mtx);
2379 sess->remote_state_data = NULL;
2380
2381 list_initialize(&sess->exch_list);
2382 fibril_mutex_initialize(&sess->mutex);
2383 atomic_set(&sess->refcnt, 0);
2384
2385 return sess;
2386}
2387
2388/** Connect to a task specified by id.
2389 *
2390 */
2391async_sess_t *async_connect_kbox(task_id_t id)
2392{
2393 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2394 if (sess == NULL) {
2395 errno = ENOMEM;
2396 return NULL;
2397 }
2398
2399 cap_phone_handle_t phone;
2400 errno_t rc = ipc_connect_kbox(id, &phone);
2401 if (rc != EOK) {
2402 errno = rc;
2403 free(sess);
2404 return NULL;
2405 }
2406
2407 sess->iface = 0;
2408 sess->mgmt = EXCHANGE_ATOMIC;
2409 sess->phone = phone;
2410 sess->arg1 = 0;
2411 sess->arg2 = 0;
2412 sess->arg3 = 0;
2413
2414 fibril_mutex_initialize(&sess->remote_state_mtx);
2415 sess->remote_state_data = NULL;
2416
2417 list_initialize(&sess->exch_list);
2418 fibril_mutex_initialize(&sess->mutex);
2419 atomic_set(&sess->refcnt, 0);
2420
2421 return sess;
2422}
2423
2424static errno_t async_hangup_internal(cap_phone_handle_t phone)
2425{
2426 return ipc_hangup(phone);
2427}
2428
2429/** Wrapper for ipc_hangup.
2430 *
2431 * @param sess Session to hung up.
2432 *
2433 * @return Zero on success or an error code.
2434 *
2435 */
2436errno_t async_hangup(async_sess_t *sess)
2437{
2438 async_exch_t *exch;
2439
2440 assert(sess);
2441
2442 if (atomic_get(&sess->refcnt) > 0)
2443 return EBUSY;
2444
2445 fibril_mutex_lock(&async_sess_mutex);
2446
2447 errno_t rc = async_hangup_internal(sess->phone);
2448
2449 while (!list_empty(&sess->exch_list)) {
2450 exch = (async_exch_t *)
2451 list_get_instance(list_first(&sess->exch_list),
2452 async_exch_t, sess_link);
2453
2454 list_remove(&exch->sess_link);
2455 list_remove(&exch->global_link);
2456 async_hangup_internal(exch->phone);
2457 free(exch);
2458 }
2459
2460 free(sess);
2461
2462 fibril_mutex_unlock(&async_sess_mutex);
2463
2464 return rc;
2465}
2466
2467/** Interrupt one thread of this task from waiting for IPC. */
2468void async_poke(void)
2469{
2470 ipc_poke();
2471}
2472
2473/** Start new exchange in a session.
2474 *
2475 * @param session Session.
2476 *
2477 * @return New exchange or NULL on error.
2478 *
2479 */
2480async_exch_t *async_exchange_begin(async_sess_t *sess)
2481{
2482 if (sess == NULL)
2483 return NULL;
2484
2485 exch_mgmt_t mgmt = sess->mgmt;
2486 if (sess->iface != 0)
2487 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
2488
2489 async_exch_t *exch = NULL;
2490
2491 fibril_mutex_lock(&async_sess_mutex);
2492
2493 if (!list_empty(&sess->exch_list)) {
2494 /*
2495 * There are inactive exchanges in the session.
2496 */
2497 exch = (async_exch_t *)
2498 list_get_instance(list_first(&sess->exch_list),
2499 async_exch_t, sess_link);
2500
2501 list_remove(&exch->sess_link);
2502 list_remove(&exch->global_link);
2503 } else {
2504 /*
2505 * There are no available exchanges in the session.
2506 */
2507
2508 if ((mgmt == EXCHANGE_ATOMIC) ||
2509 (mgmt == EXCHANGE_SERIALIZE)) {
2510 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
2511 if (exch != NULL) {
2512 link_initialize(&exch->sess_link);
2513 link_initialize(&exch->global_link);
2514 exch->sess = sess;
2515 exch->phone = sess->phone;
2516 }
2517 } else if (mgmt == EXCHANGE_PARALLEL) {
2518 cap_phone_handle_t phone;
2519 errno_t rc;
2520
2521 retry:
2522 /*
2523 * Make a one-time attempt to connect a new data phone.
2524 */
2525 rc = async_connect_me_to_internal(sess->phone, sess->arg1,
2526 sess->arg2, sess->arg3, 0, &phone);
2527 if (rc == EOK) {
2528 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
2529 if (exch != NULL) {
2530 link_initialize(&exch->sess_link);
2531 link_initialize(&exch->global_link);
2532 exch->sess = sess;
2533 exch->phone = phone;
2534 } else
2535 async_hangup_internal(phone);
2536 } else if (!list_empty(&inactive_exch_list)) {
2537 /*
2538 * We did not manage to connect a new phone. But we
2539 * can try to close some of the currently inactive
2540 * connections in other sessions and try again.
2541 */
2542 exch = (async_exch_t *)
2543 list_get_instance(list_first(&inactive_exch_list),
2544 async_exch_t, global_link);
2545
2546 list_remove(&exch->sess_link);
2547 list_remove(&exch->global_link);
2548 async_hangup_internal(exch->phone);
2549 free(exch);
2550 goto retry;
2551 } else {
2552 /*
2553 * Wait for a phone to become available.
2554 */
2555 fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
2556 goto retry;
2557 }
2558 }
2559 }
2560
2561 fibril_mutex_unlock(&async_sess_mutex);
2562
2563 if (exch != NULL) {
2564 atomic_inc(&sess->refcnt);
2565
2566 if (mgmt == EXCHANGE_SERIALIZE)
2567 fibril_mutex_lock(&sess->mutex);
2568 }
2569
2570 return exch;
2571}
2572
2573/** Finish an exchange.
2574 *
2575 * @param exch Exchange to finish.
2576 *
2577 */
2578void async_exchange_end(async_exch_t *exch)
2579{
2580 if (exch == NULL)
2581 return;
2582
2583 async_sess_t *sess = exch->sess;
2584 assert(sess != NULL);
2585
2586 exch_mgmt_t mgmt = sess->mgmt;
2587 if (sess->iface != 0)
2588 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
2589
2590 atomic_dec(&sess->refcnt);
2591
2592 if (mgmt == EXCHANGE_SERIALIZE)
2593 fibril_mutex_unlock(&sess->mutex);
2594
2595 fibril_mutex_lock(&async_sess_mutex);
2596
2597 list_append(&exch->sess_link, &sess->exch_list);
2598 list_append(&exch->global_link, &inactive_exch_list);
2599 fibril_condvar_signal(&avail_phone_cv);
2600
2601 fibril_mutex_unlock(&async_sess_mutex);
2602}
2603
2604/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
2605 *
2606 * @param exch Exchange for sending the message.
2607 * @param size Size of the destination address space area.
2608 * @param arg User defined argument.
2609 * @param flags Storage for the received flags. Can be NULL.
2610 * @param dst Address of the storage for the destination address space area
2611 * base address. Cannot be NULL.
2612 *
2613 * @return Zero on success or an error code from errno.h.
2614 *
2615 */
2616errno_t async_share_in_start(async_exch_t *exch, size_t size, sysarg_t arg,
2617 unsigned int *flags, void **dst)
2618{
2619 if (exch == NULL)
2620 return ENOENT;
2621
2622 sysarg_t _flags = 0;
2623 sysarg_t _dst = (sysarg_t) -1;
2624 errno_t res = async_req_2_4(exch, IPC_M_SHARE_IN, (sysarg_t) size,
2625 arg, NULL, &_flags, NULL, &_dst);
2626
2627 if (flags)
2628 *flags = (unsigned int) _flags;
2629
2630 *dst = (void *) _dst;
2631 return res;
2632}
2633
2634/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
2635 *
2636 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
2637 * calls so that the user doesn't have to remember the meaning of each IPC
2638 * argument.
2639 *
2640 * So far, this wrapper is to be used from within a connection fibril.
2641 *
2642 * @param chandle Storage for the handle of the IPC_M_SHARE_IN call.
2643 * @param size Destination address space area size.
2644 *
2645 * @return True on success, false on failure.
2646 *
2647 */
2648bool async_share_in_receive(cap_call_handle_t *chandle, size_t *size)
2649{
2650 assert(chandle);
2651 assert(size);
2652
2653 ipc_call_t data;
2654 *chandle = async_get_call(&data);
2655
2656 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
2657 return false;
2658
2659 *size = (size_t) IPC_GET_ARG1(data);
2660 return true;
2661}
2662
2663/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
2664 *
2665 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
2666 * calls so that the user doesn't have to remember the meaning of each IPC
2667 * argument.
2668 *
2669 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
2670 * @param src Source address space base.
2671 * @param flags Flags to be used for sharing. Bits can be only cleared.
2672 *
2673 * @return Zero on success or a value from @ref errno.h on failure.
2674 *
2675 */
2676errno_t async_share_in_finalize(cap_call_handle_t chandle, void *src,
2677 unsigned int flags)
2678{
2679 return ipc_answer_3(chandle, EOK, (sysarg_t) src, (sysarg_t) flags,
2680 (sysarg_t) __entry);
2681}
2682
2683/** Wrapper for IPC_M_SHARE_OUT calls using the async framework.
2684 *
2685 * @param exch Exchange for sending the message.
2686 * @param src Source address space area base address.
2687 * @param flags Flags to be used for sharing. Bits can be only cleared.
2688 *
2689 * @return Zero on success or an error code from errno.h.
2690 *
2691 */
2692errno_t async_share_out_start(async_exch_t *exch, void *src, unsigned int flags)
2693{
2694 if (exch == NULL)
2695 return ENOENT;
2696
2697 return async_req_3_0(exch, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
2698 (sysarg_t) flags);
2699}
2700
2701/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
2702 *
2703 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
2704 * calls so that the user doesn't have to remember the meaning of each IPC
2705 * argument.
2706 *
2707 * So far, this wrapper is to be used from within a connection fibril.
2708 *
2709 * @param chandle Storage for the hash of the IPC_M_SHARE_OUT call.
2710 * @param size Storage for the source address space area size.
2711 * @param flags Storage for the sharing flags.
2712 *
2713 * @return True on success, false on failure.
2714 *
2715 */
2716bool async_share_out_receive(cap_call_handle_t *chandle, size_t *size,
2717 unsigned int *flags)
2718{
2719 assert(chandle);
2720 assert(size);
2721 assert(flags);
2722
2723 ipc_call_t data;
2724 *chandle = async_get_call(&data);
2725
2726 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
2727 return false;
2728
2729 *size = (size_t) IPC_GET_ARG2(data);
2730 *flags = (unsigned int) IPC_GET_ARG3(data);
2731 return true;
2732}
2733
2734/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
2735 *
2736 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
2737 * calls so that the user doesn't have to remember the meaning of each IPC
2738 * argument.
2739 *
2740 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
2741 * @param dst Address of the storage for the destination address space area
2742 * base address.
2743 *
2744 * @return Zero on success or a value from @ref errno.h on failure.
2745 *
2746 */
2747errno_t async_share_out_finalize(cap_call_handle_t chandle, void **dst)
2748{
2749 return ipc_answer_2(chandle, EOK, (sysarg_t) __entry, (sysarg_t) dst);
2750}
2751
2752/** Start IPC_M_DATA_READ using the async framework.
2753 *
2754 * @param exch Exchange for sending the message.
2755 * @param dst Address of the beginning of the destination buffer.
2756 * @param size Size of the destination buffer (in bytes).
2757 * @param dataptr Storage of call data (arg 2 holds actual data size).
2758 *
2759 * @return Hash of the sent message or 0 on error.
2760 *
2761 */
2762aid_t async_data_read(async_exch_t *exch, void *dst, size_t size,
2763 ipc_call_t *dataptr)
2764{
2765 return async_send_2(exch, IPC_M_DATA_READ, (sysarg_t) dst,
2766 (sysarg_t) size, dataptr);
2767}
2768
2769/** Wrapper for IPC_M_DATA_READ calls using the async framework.
2770 *
2771 * @param exch Exchange for sending the message.
2772 * @param dst Address of the beginning of the destination buffer.
2773 * @param size Size of the destination buffer.
2774 *
2775 * @return Zero on success or an error code from errno.h.
2776 *
2777 */
2778errno_t async_data_read_start(async_exch_t *exch, void *dst, size_t size)
2779{
2780 if (exch == NULL)
2781 return ENOENT;
2782
2783 return async_req_2_0(exch, IPC_M_DATA_READ, (sysarg_t) dst,
2784 (sysarg_t) size);
2785}
2786
2787/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
2788 *
2789 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
2790 * calls so that the user doesn't have to remember the meaning of each IPC
2791 * argument.
2792 *
2793 * So far, this wrapper is to be used from within a connection fibril.
2794 *
2795 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
2796 * @param size Storage for the maximum size. Can be NULL.
2797 *
2798 * @return True on success, false on failure.
2799 *
2800 */
2801bool async_data_read_receive(cap_call_handle_t *chandle, size_t *size)
2802{
2803 ipc_call_t data;
2804 return async_data_read_receive_call(chandle, &data, size);
2805}
2806
2807/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
2808 *
2809 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
2810 * calls so that the user doesn't have to remember the meaning of each IPC
2811 * argument.
2812 *
2813 * So far, this wrapper is to be used from within a connection fibril.
2814 *
2815 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
2816 * @param size Storage for the maximum size. Can be NULL.
2817 *
2818 * @return True on success, false on failure.
2819 *
2820 */
2821bool async_data_read_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
2822 size_t *size)
2823{
2824 assert(chandle);
2825 assert(data);
2826
2827 *chandle = async_get_call(data);
2828
2829 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
2830 return false;
2831
2832 if (size)
2833 *size = (size_t) IPC_GET_ARG2(*data);
2834
2835 return true;
2836}
2837
2838/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
2839 *
2840 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
2841 * calls so that the user doesn't have to remember the meaning of each IPC
2842 * argument.
2843 *
2844 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
2845 * @param src Source address for the IPC_M_DATA_READ call.
2846 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
2847 * the maximum size announced by the sender.
2848 *
2849 * @return Zero on success or a value from @ref errno.h on failure.
2850 *
2851 */
2852errno_t async_data_read_finalize(cap_call_handle_t chandle, const void *src,
2853 size_t size)
2854{
2855 return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
2856}
2857
2858/** Wrapper for forwarding any read request
2859 *
2860 */
2861errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
2862 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
2863 ipc_call_t *dataptr)
2864{
2865 if (exch == NULL)
2866 return ENOENT;
2867
2868 cap_call_handle_t chandle;
2869 if (!async_data_read_receive(&chandle, NULL)) {
2870 ipc_answer_0(chandle, EINVAL);
2871 return EINVAL;
2872 }
2873
2874 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
2875 dataptr);
2876 if (msg == 0) {
2877 ipc_answer_0(chandle, EINVAL);
2878 return EINVAL;
2879 }
2880
2881 errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
2882 IPC_FF_ROUTE_FROM_ME);
2883 if (retval != EOK) {
2884 async_forget(msg);
2885 ipc_answer_0(chandle, retval);
2886 return retval;
2887 }
2888
2889 errno_t rc;
2890 async_wait_for(msg, &rc);
2891
2892 return (errno_t) rc;
2893}
2894
2895/** Wrapper for IPC_M_DATA_WRITE calls using the async framework.
2896 *
2897 * @param exch Exchange for sending the message.
2898 * @param src Address of the beginning of the source buffer.
2899 * @param size Size of the source buffer.
2900 *
2901 * @return Zero on success or an error code from errno.h.
2902 *
2903 */
2904errno_t async_data_write_start(async_exch_t *exch, const void *src, size_t size)
2905{
2906 if (exch == NULL)
2907 return ENOENT;
2908
2909 return async_req_2_0(exch, IPC_M_DATA_WRITE, (sysarg_t) src,
2910 (sysarg_t) size);
2911}
2912
2913/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
2914 *
2915 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
2916 * calls so that the user doesn't have to remember the meaning of each IPC
2917 * argument.
2918 *
2919 * So far, this wrapper is to be used from within a connection fibril.
2920 *
2921 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
2922 * @param size Storage for the suggested size. May be NULL.
2923 *
2924 * @return True on success, false on failure.
2925 *
2926 */
2927bool async_data_write_receive(cap_call_handle_t *chandle, size_t *size)
2928{
2929 ipc_call_t data;
2930 return async_data_write_receive_call(chandle, &data, size);
2931}
2932
2933/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
2934 *
2935 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
2936 * calls so that the user doesn't have to remember the meaning of each IPC
2937 * argument.
2938 *
2939 * So far, this wrapper is to be used from within a connection fibril.
2940 *
2941 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
2942 * @param data Storage for the ipc call data.
2943 * @param size Storage for the suggested size. May be NULL.
2944 *
2945 * @return True on success, false on failure.
2946 *
2947 */
2948bool async_data_write_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
2949 size_t *size)
2950{
2951 assert(chandle);
2952 assert(data);
2953
2954 *chandle = async_get_call(data);
2955
2956 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
2957 return false;
2958
2959 if (size)
2960 *size = (size_t) IPC_GET_ARG2(*data);
2961
2962 return true;
2963}
2964
2965/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
2966 *
2967 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
2968 * calls so that the user doesn't have to remember the meaning of each IPC
2969 * argument.
2970 *
2971 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
2972 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
2973 * @param size Final size for the IPC_M_DATA_WRITE call.
2974 *
2975 * @return Zero on success or a value from @ref errno.h on failure.
2976 *
2977 */
2978errno_t async_data_write_finalize(cap_call_handle_t chandle, void *dst,
2979 size_t size)
2980{
2981 return ipc_answer_2(chandle, EOK, (sysarg_t) dst, (sysarg_t) size);
2982}
2983
2984/** Wrapper for receiving binary data or strings
2985 *
2986 * This wrapper only makes it more comfortable to use async_data_write_*
2987 * functions to receive binary data or strings.
2988 *
2989 * @param data Pointer to data pointer (which should be later disposed
2990 * by free()). If the operation fails, the pointer is not
2991 * touched.
2992 * @param nullterm If true then the received data is always zero terminated.
2993 * This also causes to allocate one extra byte beyond the
2994 * raw transmitted data.
2995 * @param min_size Minimum size (in bytes) of the data to receive.
2996 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
2997 * no limit.
2998 * @param granulariy If non-zero then the size of the received data has to
2999 * be divisible by this value.
3000 * @param received If not NULL, the size of the received data is stored here.
3001 *
3002 * @return Zero on success or a value from @ref errno.h on failure.
3003 *
3004 */
3005errno_t async_data_write_accept(void **data, const bool nullterm,
3006 const size_t min_size, const size_t max_size, const size_t granularity,
3007 size_t *received)
3008{
3009 assert(data);
3010
3011 cap_call_handle_t chandle;
3012 size_t size;
3013 if (!async_data_write_receive(&chandle, &size)) {
3014 ipc_answer_0(chandle, EINVAL);
3015 return EINVAL;
3016 }
3017
3018 if (size < min_size) {
3019 ipc_answer_0(chandle, EINVAL);
3020 return EINVAL;
3021 }
3022
3023 if ((max_size > 0) && (size > max_size)) {
3024 ipc_answer_0(chandle, EINVAL);
3025 return EINVAL;
3026 }
3027
3028 if ((granularity > 0) && ((size % granularity) != 0)) {
3029 ipc_answer_0(chandle, EINVAL);
3030 return EINVAL;
3031 }
3032
3033 void *arg_data;
3034
3035 if (nullterm)
3036 arg_data = malloc(size + 1);
3037 else
3038 arg_data = malloc(size);
3039
3040 if (arg_data == NULL) {
3041 ipc_answer_0(chandle, ENOMEM);
3042 return ENOMEM;
3043 }
3044
3045 errno_t rc = async_data_write_finalize(chandle, arg_data, size);
3046 if (rc != EOK) {
3047 free(arg_data);
3048 return rc;
3049 }
3050
3051 if (nullterm)
3052 ((char *) arg_data)[size] = 0;
3053
3054 *data = arg_data;
3055 if (received != NULL)
3056 *received = size;
3057
3058 return EOK;
3059}
3060
3061/** Wrapper for voiding any data that is about to be received
3062 *
3063 * This wrapper can be used to void any pending data
3064 *
3065 * @param retval Error value from @ref errno.h to be returned to the caller.
3066 *
3067 */
3068void async_data_write_void(errno_t retval)
3069{
3070 cap_call_handle_t chandle;
3071 async_data_write_receive(&chandle, NULL);
3072 ipc_answer_0(chandle, retval);
3073}
3074
3075/** Wrapper for forwarding any data that is about to be received
3076 *
3077 */
3078errno_t async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
3079 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
3080 ipc_call_t *dataptr)
3081{
3082 if (exch == NULL)
3083 return ENOENT;
3084
3085 cap_call_handle_t chandle;
3086 if (!async_data_write_receive(&chandle, NULL)) {
3087 ipc_answer_0(chandle, EINVAL);
3088 return EINVAL;
3089 }
3090
3091 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
3092 dataptr);
3093 if (msg == 0) {
3094 ipc_answer_0(chandle, EINVAL);
3095 return EINVAL;
3096 }
3097
3098 errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
3099 IPC_FF_ROUTE_FROM_ME);
3100 if (retval != EOK) {
3101 async_forget(msg);
3102 ipc_answer_0(chandle, retval);
3103 return retval;
3104 }
3105
3106 errno_t rc;
3107 async_wait_for(msg, &rc);
3108
3109 return (errno_t) rc;
3110}
3111
3112/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
3113 *
3114 * If the current call is IPC_M_CONNECT_TO_ME then a new
3115 * async session is created for the accepted phone.
3116 *
3117 * @param mgmt Exchange management style.
3118 *
3119 * @return New async session.
3120 * @return NULL on failure.
3121 *
3122 */
3123async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
3124{
3125 /* Accept the phone */
3126 ipc_call_t call;
3127 cap_call_handle_t chandle = async_get_call(&call);
3128 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
3129
3130 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) ||
3131 !CAP_HANDLE_VALID((phandle))) {
3132 async_answer_0(chandle, EINVAL);
3133 return NULL;
3134 }
3135
3136 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
3137 if (sess == NULL) {
3138 async_answer_0(chandle, ENOMEM);
3139 return NULL;
3140 }
3141
3142 sess->iface = 0;
3143 sess->mgmt = mgmt;
3144 sess->phone = phandle;
3145 sess->arg1 = 0;
3146 sess->arg2 = 0;
3147 sess->arg3 = 0;
3148
3149 fibril_mutex_initialize(&sess->remote_state_mtx);
3150 sess->remote_state_data = NULL;
3151
3152 list_initialize(&sess->exch_list);
3153 fibril_mutex_initialize(&sess->mutex);
3154 atomic_set(&sess->refcnt, 0);
3155
3156 /* Acknowledge the connected phone */
3157 async_answer_0(chandle, EOK);
3158
3159 return sess;
3160}
3161
3162/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
3163 *
3164 * If the call is IPC_M_CONNECT_TO_ME then a new
3165 * async session is created. However, the phone is
3166 * not accepted automatically.
3167 *
3168 * @param mgmt Exchange management style.
3169 * @param call Call data.
3170 *
3171 * @return New async session.
3172 * @return NULL on failure.
3173 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
3174 *
3175 */
3176async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
3177{
3178 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
3179
3180 if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) ||
3181 !CAP_HANDLE_VALID((phandle)))
3182 return NULL;
3183
3184 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
3185 if (sess == NULL)
3186 return NULL;
3187
3188 sess->iface = 0;
3189 sess->mgmt = mgmt;
3190 sess->phone = phandle;
3191 sess->arg1 = 0;
3192 sess->arg2 = 0;
3193 sess->arg3 = 0;
3194
3195 fibril_mutex_initialize(&sess->remote_state_mtx);
3196 sess->remote_state_data = NULL;
3197
3198 list_initialize(&sess->exch_list);
3199 fibril_mutex_initialize(&sess->mutex);
3200 atomic_set(&sess->refcnt, 0);
3201
3202 return sess;
3203}
3204
3205errno_t async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
3206 sysarg_t arg3, async_exch_t *other_exch)
3207{
3208 return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
3209 arg1, arg2, arg3, 0, CAP_HANDLE_RAW(other_exch->phone));
3210}
3211
3212bool async_state_change_receive(cap_call_handle_t *chandle, sysarg_t *arg1,
3213 sysarg_t *arg2, sysarg_t *arg3)
3214{
3215 assert(chandle);
3216
3217 ipc_call_t call;
3218 *chandle = async_get_call(&call);
3219
3220 if (IPC_GET_IMETHOD(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
3221 return false;
3222
3223 if (arg1)
3224 *arg1 = IPC_GET_ARG1(call);
3225 if (arg2)
3226 *arg2 = IPC_GET_ARG2(call);
3227 if (arg3)
3228 *arg3 = IPC_GET_ARG3(call);
3229
3230 return true;
3231}
3232
3233errno_t async_state_change_finalize(cap_call_handle_t chandle,
3234 async_exch_t *other_exch)
3235{
3236 return ipc_answer_1(chandle, EOK, CAP_HANDLE_RAW(other_exch->phone));
3237}
3238
3239/** Lock and get session remote state
3240 *
3241 * Lock and get the local replica of the remote state
3242 * in stateful sessions. The call should be paired
3243 * with async_remote_state_release*().
3244 *
3245 * @param[in] sess Stateful session.
3246 *
3247 * @return Local replica of the remote state.
3248 *
3249 */
3250void *async_remote_state_acquire(async_sess_t *sess)
3251{
3252 fibril_mutex_lock(&sess->remote_state_mtx);
3253 return sess->remote_state_data;
3254}
3255
3256/** Update the session remote state
3257 *
3258 * Update the local replica of the remote state
3259 * in stateful sessions. The remote state must
3260 * be already locked.
3261 *
3262 * @param[in] sess Stateful session.
3263 * @param[in] state New local replica of the remote state.
3264 *
3265 */
3266void async_remote_state_update(async_sess_t *sess, void *state)
3267{
3268 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
3269 sess->remote_state_data = state;
3270}
3271
3272/** Release the session remote state
3273 *
3274 * Unlock the local replica of the remote state
3275 * in stateful sessions.
3276 *
3277 * @param[in] sess Stateful session.
3278 *
3279 */
3280void async_remote_state_release(async_sess_t *sess)
3281{
3282 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
3283
3284 fibril_mutex_unlock(&sess->remote_state_mtx);
3285}
3286
3287/** Release the session remote state and end an exchange
3288 *
3289 * Unlock the local replica of the remote state
3290 * in stateful sessions. This is convenience function
3291 * which gets the session pointer from the exchange
3292 * and also ends the exchange.
3293 *
3294 * @param[in] exch Stateful session's exchange.
3295 *
3296 */
3297void async_remote_state_release_exchange(async_exch_t *exch)
3298{
3299 if (exch == NULL)
3300 return;
3301
3302 async_sess_t *sess = exch->sess;
3303 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
3304
3305 async_exchange_end(exch);
3306 fibril_mutex_unlock(&sess->remote_state_mtx);
3307}
3308
3309void *async_as_area_create(void *base, size_t size, unsigned int flags,
3310 async_sess_t *pager, sysarg_t id1, sysarg_t id2, sysarg_t id3)
3311{
3312 as_area_pager_info_t pager_info = {
3313 .pager = pager->phone,
3314 .id1 = id1,
3315 .id2 = id2,
3316 .id3 = id3
3317 };
3318 return as_area_create(base, size, flags, &pager_info);
3319}
3320
3321/** @}
3322 */
Note: See TracBrowser for help on using the repository browser.