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

Last change on this file since 5c38838 was 5c38838, checked in by Jakub Jermar <jakub@…>, 7 years ago

Answer the IPC_M_PHONE_HUNGUP call

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