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

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

Avoid including <fibril.h> from <async.h>

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