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

Last change on this file since 7fa8589 was 7fa8589, checked in by Matthieu Riolo <matthieu.riolo@…>, 5 years ago

Removing unneeded casts from errno_t to errno_t

  • Property mode set to 100644
File size: 45.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(const void *key)
234{
235 const task_id_t *in_task_id = 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(const void *key, const ht_link_t *item)
246{
247 const task_id_t *in_task_id = 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, it's
393 * either a callback connection that was opened by
394 * accepting the IPC_M_CONNECT_TO_ME call.
395 * @param handler Connection handler.
396 * @param data Client argument to pass to the connection handler.
397 *
398 * @return New fibril id or NULL on failure.
399 *
400 */
401static fid_t async_new_connection(connection_t *conn, task_id_t in_task_id,
402 ipc_call_t *call, async_port_handler_t handler, void *data)
403{
404 conn->in_task_id = in_task_id;
405 conn->msg_channel = mpsc_create(sizeof(ipc_call_t));
406 conn->handler = handler;
407 conn->data = data;
408
409 if (!conn->msg_channel)
410 goto error;
411
412 if (call)
413 conn->call = *call;
414 else
415 conn->call.cap_handle = CAP_NIL;
416
417 /* We will activate the fibril ASAP */
418 conn->fid = fibril_create(connection_fibril, conn);
419
420 if (conn->fid == 0)
421 goto error;
422
423 fibril_start(conn->fid);
424
425 return conn->fid;
426
427error:
428 if (conn->msg_channel)
429 mpsc_destroy(conn->msg_channel);
430 free(conn);
431
432 if (call)
433 ipc_answer_0(call->cap_handle, ENOMEM);
434
435 return (fid_t) NULL;
436}
437
438/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
439 *
440 * Ask through phone for a new connection to some service.
441 *
442 * @param exch Exchange for sending the message.
443 * @param iface Callback interface.
444 * @param arg1 User defined argument.
445 * @param arg2 User defined argument.
446 * @param handler Callback handler.
447 * @param data Handler data.
448 * @param port_id ID of the newly created port.
449 *
450 * @return Zero on success or an error code.
451 *
452 */
453errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
454 sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
455{
456 if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
457 return EINVAL;
458
459 if (exch == NULL)
460 return ENOENT;
461
462 connection_t *conn = calloc(1, sizeof(*conn));
463 if (!conn)
464 return ENOMEM;
465
466 ipc_call_t answer;
467 aid_t req = async_send_5(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
468 0, (sysarg_t) conn, &answer);
469
470 errno_t rc;
471 async_wait_for(req, &rc);
472 if (rc != EOK) {
473 free(conn);
474 return rc;
475 }
476
477 rc = async_create_port_internal(iface, handler, data, port_id);
478 if (rc != EOK) {
479 free(conn);
480 return rc;
481 }
482
483 fid_t fid = async_new_connection(conn, answer.task_id, NULL, handler,
484 data);
485 if (fid == (fid_t) NULL)
486 return ENOMEM;
487
488 return EOK;
489}
490
491static size_t notification_key_hash(const void *key)
492{
493 const sysarg_t *id = key;
494 return *id;
495}
496
497static size_t notification_hash(const ht_link_t *item)
498{
499 notification_t *notification =
500 hash_table_get_inst(item, notification_t, htlink);
501 return notification_key_hash(&notification->imethod);
502}
503
504static bool notification_key_equal(const void *key, const ht_link_t *item)
505{
506 const sysarg_t *id = key;
507 notification_t *notification =
508 hash_table_get_inst(item, notification_t, htlink);
509 return *id == notification->imethod;
510}
511
512/** Operations for the notification hash table. */
513static hash_table_ops_t notification_hash_table_ops = {
514 .hash = notification_hash,
515 .key_hash = notification_key_hash,
516 .key_equal = notification_key_equal,
517 .equal = NULL,
518 .remove_callback = NULL
519};
520
521/** Try to route a call to an appropriate connection fibril.
522 *
523 * If the proper connection fibril is found, a message with the call is added to
524 * its message queue. If the fibril was not active, it is activated and all
525 * timeouts are unregistered.
526 *
527 * @param call Data of the incoming call.
528 *
529 * @return EOK if the call was successfully passed to the respective fibril.
530 * @return ENOENT if the call doesn't match any connection.
531 * @return Other error code if routing failed for other reasons.
532 *
533 */
534static errno_t route_call(ipc_call_t *call)
535{
536 assert(call);
537
538 connection_t *conn = (connection_t *) call->request_label;
539
540 if (!conn)
541 return ENOENT;
542
543 assert(conn->msg_channel);
544
545 errno_t rc = mpsc_send(conn->msg_channel, call);
546
547 if (ipc_get_imethod(call) == IPC_M_PHONE_HUNGUP) {
548 /* Close the channel, but let the connection fibril answer. */
549 mpsc_close(conn->msg_channel);
550 // FIXME: Ideally, we should be able to discard/answer the
551 // hungup message here and just close the channel without
552 // passing it out. Unfortunatelly, somehow that breaks
553 // handling of CPU exceptions.
554 }
555
556 return rc;
557}
558
559/** Function implementing the notification handler fibril. Never returns. */
560static errno_t notification_fibril_func(void *arg)
561{
562 (void) arg;
563
564 while (true) {
565 fibril_semaphore_down(&notification_semaphore);
566
567 fibril_rmutex_lock(&notification_mutex);
568
569 /*
570 * The semaphore ensures that if we get this far,
571 * the queue must be non-empty.
572 */
573 assert(!list_empty(&notification_queue));
574
575 notification_t *notification = list_get_instance(
576 list_first(&notification_queue), notification_t, qlink);
577
578 async_notification_handler_t handler = notification->handler;
579 void *arg = notification->arg;
580
581 notification_msg_t *m = list_pop(&notification->msg_list,
582 notification_msg_t, link);
583 assert(m);
584 ipc_call_t calldata = m->calldata;
585
586 notification_freelist_used--;
587
588 if (notification_freelist_total > 64 &&
589 notification_freelist_total > 2 * notification_freelist_used) {
590 /* Going to free the structure if we have too much. */
591 notification_freelist_total--;
592 } else {
593 /* Otherwise add to freelist. */
594 list_append(&m->link, &notification_freelist);
595 m = NULL;
596 }
597
598 if (list_empty(&notification->msg_list))
599 list_remove(&notification->qlink);
600
601 fibril_rmutex_unlock(&notification_mutex);
602
603 if (handler)
604 handler(&calldata, arg);
605
606 free(m);
607 }
608
609 /* Not reached. */
610 return EOK;
611}
612
613/**
614 * Creates a new dedicated fibril for handling notifications.
615 * By default, there is one such fibril. This function can be used to
616 * create more in order to increase the number of notification that can
617 * be processed concurrently.
618 *
619 * Currently, there is no way to destroy those fibrils after they are created.
620 */
621errno_t async_spawn_notification_handler(void)
622{
623 fid_t f = fibril_create(notification_fibril_func, NULL);
624 if (f == 0)
625 return ENOMEM;
626
627 fibril_add_ready(f);
628 return EOK;
629}
630
631/** Queue notification.
632 *
633 * @param call Data of the incoming call.
634 *
635 */
636static void queue_notification(ipc_call_t *call)
637{
638 assert(call);
639
640 fibril_rmutex_lock(&notification_mutex);
641
642 notification_msg_t *m = list_pop(&notification_freelist,
643 notification_msg_t, link);
644
645 if (!m) {
646 fibril_rmutex_unlock(&notification_mutex);
647 m = malloc(sizeof(notification_msg_t));
648 if (!m) {
649 DPRINTF("Out of memory.\n");
650 abort();
651 }
652
653 fibril_rmutex_lock(&notification_mutex);
654 notification_freelist_total++;
655 }
656
657 sysarg_t imethod = ipc_get_imethod(call);
658 ht_link_t *link = hash_table_find(&notification_hash_table, &imethod);
659 if (!link) {
660 /* Invalid notification. */
661 // TODO: Make sure this can't happen and turn it into assert.
662 notification_freelist_total--;
663 fibril_rmutex_unlock(&notification_mutex);
664 free(m);
665 return;
666 }
667
668 notification_t *notification =
669 hash_table_get_inst(link, notification_t, htlink);
670
671 notification_freelist_used++;
672 m->calldata = *call;
673 list_append(&m->link, &notification->msg_list);
674
675 if (!link_in_use(&notification->qlink))
676 list_append(&notification->qlink, &notification_queue);
677
678 fibril_rmutex_unlock(&notification_mutex);
679
680 fibril_semaphore_up(&notification_semaphore);
681}
682
683/**
684 * Creates a new notification structure and inserts it into the hash table.
685 *
686 * @param handler Function to call when notification is received.
687 * @param arg Argument for the handler function.
688 * @return The newly created notification structure.
689 */
690static notification_t *notification_create(async_notification_handler_t handler, void *arg)
691{
692 notification_t *notification = calloc(1, sizeof(notification_t));
693 if (!notification)
694 return NULL;
695
696 notification->handler = handler;
697 notification->arg = arg;
698
699 list_initialize(&notification->msg_list);
700
701 fid_t fib = 0;
702
703 fibril_rmutex_lock(&notification_mutex);
704
705 if (notification_avail == 0) {
706 /* Attempt to create the first handler fibril. */
707 fib = fibril_create(notification_fibril_func, NULL);
708 if (fib == 0) {
709 fibril_rmutex_unlock(&notification_mutex);
710 free(notification);
711 return NULL;
712 }
713 }
714
715 sysarg_t imethod = notification_avail;
716 notification_avail++;
717
718 notification->imethod = imethod;
719 hash_table_insert(&notification_hash_table, &notification->htlink);
720
721 fibril_rmutex_unlock(&notification_mutex);
722
723 if (imethod == 0) {
724 assert(fib);
725 fibril_add_ready(fib);
726 }
727
728 return notification;
729}
730
731/** Subscribe to IRQ notification.
732 *
733 * @param inr IRQ number.
734 * @param handler Notification handler.
735 * @param data Notification handler client data.
736 * @param ucode Top-half pseudocode handler.
737 *
738 * @param[out] handle IRQ capability handle on success.
739 *
740 * @return An error code.
741 *
742 */
743errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
744 void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
745{
746 notification_t *notification = notification_create(handler, data);
747 if (!notification)
748 return ENOMEM;
749
750 cap_irq_handle_t ihandle;
751 errno_t rc = ipc_irq_subscribe(inr, notification->imethod, ucode,
752 &ihandle);
753 if (rc == EOK && handle != NULL) {
754 *handle = ihandle;
755 }
756 return rc;
757}
758
759/** Unsubscribe from IRQ notification.
760 *
761 * @param handle IRQ capability handle.
762 *
763 * @return Zero on success or an error code.
764 *
765 */
766errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
767{
768 // TODO: Remove entry from hash table
769 // to avoid memory leak
770
771 return ipc_irq_unsubscribe(ihandle);
772}
773
774/** Subscribe to event notifications.
775 *
776 * @param evno Event type to subscribe.
777 * @param handler Notification handler.
778 * @param data Notification handler client data.
779 *
780 * @return Zero on success or an error code.
781 *
782 */
783errno_t async_event_subscribe(event_type_t evno,
784 async_notification_handler_t handler, void *data)
785{
786 notification_t *notification = notification_create(handler, data);
787 if (!notification)
788 return ENOMEM;
789
790 return ipc_event_subscribe(evno, notification->imethod);
791}
792
793/** Subscribe to task event notifications.
794 *
795 * @param evno Event type to subscribe.
796 * @param handler Notification handler.
797 * @param data Notification handler client data.
798 *
799 * @return Zero on success or an error code.
800 *
801 */
802errno_t async_event_task_subscribe(event_task_type_t evno,
803 async_notification_handler_t handler, void *data)
804{
805 notification_t *notification = notification_create(handler, data);
806 if (!notification)
807 return ENOMEM;
808
809 return ipc_event_task_subscribe(evno, notification->imethod);
810}
811
812/** Unmask event notifications.
813 *
814 * @param evno Event type to unmask.
815 *
816 * @return Value returned by the kernel.
817 *
818 */
819errno_t async_event_unmask(event_type_t evno)
820{
821 return ipc_event_unmask(evno);
822}
823
824/** Unmask task event notifications.
825 *
826 * @param evno Event type to unmask.
827 *
828 * @return Value returned by the kernel.
829 *
830 */
831errno_t async_event_task_unmask(event_task_type_t evno)
832{
833 return ipc_event_task_unmask(evno);
834}
835
836/** Return new incoming message for the current (fibril-local) connection.
837 *
838 * @param call Storage where the incoming call data will be stored.
839 * @param usecs Timeout in microseconds. Zero denotes no timeout.
840 *
841 * @return If no timeout was specified, then true is returned.
842 * @return If a timeout is specified, then true is returned unless
843 * the timeout expires prior to receiving a message.
844 *
845 */
846bool async_get_call_timeout(ipc_call_t *call, usec_t usecs)
847{
848 assert(call);
849 assert(fibril_connection);
850
851 struct timespec ts;
852 struct timespec *expires = NULL;
853 if (usecs) {
854 getuptime(&ts);
855 ts_add_diff(&ts, USEC2NSEC(usecs));
856 expires = &ts;
857 }
858
859 errno_t rc = mpsc_receive(fibril_connection->msg_channel,
860 call, expires);
861
862 if (rc == ETIMEOUT)
863 return false;
864
865 if (rc != EOK) {
866 /*
867 * The async_get_call_timeout() interface doesn't support
868 * propagating errors. Return a null call instead.
869 */
870
871 memset(call, 0, sizeof(ipc_call_t));
872 ipc_set_imethod(call, IPC_M_PHONE_HUNGUP);
873 call->cap_handle = CAP_NIL;
874 }
875
876 return true;
877}
878
879bool async_get_call(ipc_call_t *call)
880{
881 return async_get_call_timeout(call, 0);
882}
883
884void *async_get_client_data(void)
885{
886 assert(fibril_connection);
887 return fibril_connection->client->data;
888}
889
890void *async_get_client_data_by_id(task_id_t client_id)
891{
892 client_t *client = async_client_get(client_id, false);
893 if (!client)
894 return NULL;
895
896 if (!client->data) {
897 async_client_put(client);
898 return NULL;
899 }
900
901 return client->data;
902}
903
904void async_put_client_data_by_id(task_id_t client_id)
905{
906 client_t *client = async_client_get(client_id, false);
907
908 assert(client);
909 assert(client->data);
910
911 /* Drop the reference we got in async_get_client_data_by_hash(). */
912 async_client_put(client);
913
914 /* Drop our own reference we got at the beginning of this function. */
915 async_client_put(client);
916}
917
918/** Handle a call that was received.
919 *
920 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
921 * Otherwise the call is routed to its connection fibril.
922 *
923 * @param call Data of the incoming call.
924 *
925 */
926static void handle_call(ipc_call_t *call)
927{
928 assert(call);
929
930 if (call->flags & IPC_CALL_ANSWERED) {
931 /* Answer to a call made by us. */
932 async_reply_received(call);
933 return;
934 }
935
936 if (call->cap_handle == CAP_NIL) {
937 if (call->flags & IPC_CALL_NOTIF) {
938 /* Kernel notification */
939 queue_notification(call);
940 }
941 return;
942 }
943
944 /* New connection */
945 if (ipc_get_imethod(call) == IPC_M_CONNECT_ME_TO) {
946 connection_t *conn = calloc(1, sizeof(*conn));
947 if (!conn) {
948 ipc_answer_0(call->cap_handle, ENOMEM);
949 return;
950 }
951
952 iface_t iface = (iface_t) ipc_get_arg1(call);
953
954 // TODO: Currently ignores all ports but the first one.
955 void *data;
956 async_port_handler_t handler =
957 async_get_port_handler(iface, 0, &data);
958
959 async_new_connection(conn, call->task_id, call, handler, data);
960 return;
961 }
962
963 /* Route the call according to its request label */
964 errno_t rc = route_call(call);
965 if (rc == EOK)
966 return;
967
968 // TODO: Log the error.
969 if (call->cap_handle != CAP_NIL)
970 /* Unknown call from unknown phone - hang it up */
971 ipc_answer_0(call->cap_handle, EHANGUP);
972}
973
974/** Endless loop dispatching incoming calls and answers.
975 *
976 * @return Never returns.
977 *
978 */
979static errno_t async_manager_worker(void)
980{
981 ipc_call_t call;
982 errno_t rc;
983
984 while (true) {
985 rc = fibril_ipc_wait(&call, NULL);
986 if (rc == EOK)
987 handle_call(&call);
988 }
989
990 return 0;
991}
992
993/** Function to start async_manager as a standalone fibril.
994 *
995 * When more kernel threads are used, one async manager should exist per thread.
996 *
997 * @param arg Unused.
998 * @return Never returns.
999 *
1000 */
1001static errno_t async_manager_fibril(void *arg)
1002{
1003 async_manager_worker();
1004 return 0;
1005}
1006
1007/** Add one manager to manager list. */
1008static fid_t async_create_manager(void)
1009{
1010 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
1011 fibril_start(fid);
1012 return fid;
1013}
1014
1015/** Initialize the async framework.
1016 *
1017 */
1018void __async_server_init(void)
1019{
1020 if (fibril_rmutex_initialize(&client_mutex) != EOK)
1021 abort();
1022 if (fibril_rmutex_initialize(&notification_mutex) != EOK)
1023 abort();
1024
1025 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
1026 abort();
1027
1028 if (!hash_table_create(&notification_hash_table, 0, 0,
1029 &notification_hash_table_ops))
1030 abort();
1031
1032 async_create_manager();
1033}
1034
1035void __async_server_fini(void)
1036{
1037 fibril_rmutex_destroy(&client_mutex);
1038 fibril_rmutex_destroy(&notification_mutex);
1039}
1040
1041errno_t async_accept_0(ipc_call_t *call)
1042{
1043 cap_call_handle_t chandle = call->cap_handle;
1044 assert(chandle != CAP_NIL);
1045 call->cap_handle = CAP_NIL;
1046 return ipc_answer_5(chandle, EOK, 0, 0, 0, 0, async_get_label());
1047}
1048
1049errno_t async_answer_0(ipc_call_t *call, errno_t retval)
1050{
1051 cap_call_handle_t chandle = call->cap_handle;
1052 assert(chandle != CAP_NIL);
1053 call->cap_handle = CAP_NIL;
1054 return ipc_answer_0(chandle, retval);
1055}
1056
1057errno_t async_answer_1(ipc_call_t *call, errno_t retval, sysarg_t arg1)
1058{
1059 cap_call_handle_t chandle = call->cap_handle;
1060 assert(chandle != CAP_NIL);
1061 call->cap_handle = CAP_NIL;
1062 return ipc_answer_1(chandle, retval, arg1);
1063}
1064
1065errno_t async_answer_2(ipc_call_t *call, errno_t retval, sysarg_t arg1,
1066 sysarg_t arg2)
1067{
1068 cap_call_handle_t chandle = call->cap_handle;
1069 assert(chandle != CAP_NIL);
1070 call->cap_handle = CAP_NIL;
1071 return ipc_answer_2(chandle, retval, arg1, arg2);
1072}
1073
1074errno_t async_answer_3(ipc_call_t *call, errno_t retval, sysarg_t arg1,
1075 sysarg_t arg2, sysarg_t arg3)
1076{
1077 cap_call_handle_t chandle = call->cap_handle;
1078 assert(chandle != CAP_NIL);
1079 call->cap_handle = CAP_NIL;
1080 return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
1081}
1082
1083errno_t async_answer_4(ipc_call_t *call, errno_t retval, sysarg_t arg1,
1084 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1085{
1086 cap_call_handle_t chandle = call->cap_handle;
1087 assert(chandle != CAP_NIL);
1088 call->cap_handle = CAP_NIL;
1089 return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
1090}
1091
1092errno_t async_answer_5(ipc_call_t *call, errno_t retval, sysarg_t arg1,
1093 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
1094{
1095 cap_call_handle_t chandle = call->cap_handle;
1096 assert(chandle != CAP_NIL);
1097 call->cap_handle = CAP_NIL;
1098 return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
1099}
1100
1101static errno_t async_forward_fast(ipc_call_t *call, async_exch_t *exch,
1102 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1103{
1104 assert(call);
1105
1106 cap_call_handle_t chandle = call->cap_handle;
1107 assert(chandle != CAP_NIL);
1108 call->cap_handle = CAP_NIL;
1109
1110 if (exch == NULL)
1111 return ENOENT;
1112
1113 return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2,
1114 mode);
1115}
1116
1117static errno_t async_forward_slow(ipc_call_t *call, async_exch_t *exch,
1118 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1119 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
1120{
1121 assert(call);
1122
1123 cap_call_handle_t chandle = call->cap_handle;
1124 assert(chandle != CAP_NIL);
1125 call->cap_handle = CAP_NIL;
1126
1127 if (exch == NULL)
1128 return ENOENT;
1129
1130 return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
1131 arg4, arg5, mode);
1132}
1133
1134errno_t async_forward_0(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1135 unsigned int mode)
1136{
1137 return async_forward_fast(call, exch, imethod, 0, 0, mode);
1138}
1139
1140errno_t async_forward_1(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1141 sysarg_t arg1, unsigned int mode)
1142{
1143 return async_forward_fast(call, exch, imethod, arg1, 0, mode);
1144}
1145
1146errno_t async_forward_2(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1147 sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1148{
1149 return async_forward_fast(call, exch, imethod, arg1, arg2, mode);
1150}
1151
1152errno_t async_forward_3(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1153 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, unsigned int mode)
1154{
1155 return async_forward_slow(call, exch, imethod, arg1, arg2, arg3, 0, 0,
1156 mode);
1157}
1158
1159errno_t async_forward_4(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1160 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1161 unsigned int mode)
1162{
1163 return async_forward_slow(call, exch, imethod, arg1, arg2, arg3, arg4,
1164 0, mode);
1165}
1166
1167errno_t async_forward_5(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1168 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
1169 unsigned int mode)
1170{
1171 return async_forward_slow(call, exch, imethod, arg1, arg2, arg3, arg4,
1172 arg5, mode);
1173}
1174
1175/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
1176 *
1177 * Ask through phone for a new connection to some service.
1178 *
1179 * @param exch Exchange for sending the message.
1180 * @param iface Callback interface.
1181 * @param arg2 User defined argument.
1182 * @param arg3 User defined argument.
1183 *
1184 * @return Zero on success or an error code.
1185 *
1186 */
1187errno_t async_connect_to_me(async_exch_t *exch, iface_t iface, sysarg_t arg2,
1188 sysarg_t arg3)
1189{
1190 if (exch == NULL)
1191 return ENOENT;
1192
1193 sysarg_t label = 0;
1194 errno_t rc = async_req_5_0(exch, IPC_M_CONNECT_TO_ME, iface, arg2, arg3,
1195 0, label);
1196
1197 return rc;
1198}
1199
1200/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
1201 *
1202 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
1203 * calls so that the user doesn't have to remember the meaning of each IPC
1204 * argument.
1205 *
1206 * So far, this wrapper is to be used from within a connection fibril.
1207 *
1208 * @param call Storage for the data of the IPC_M_SHARE_IN call.
1209 * @param size Destination address space area size.
1210 *
1211 * @return True on success, false on failure.
1212 *
1213 */
1214bool async_share_in_receive(ipc_call_t *call, size_t *size)
1215{
1216 assert(call);
1217 assert(size);
1218
1219 async_get_call(call);
1220
1221 if (ipc_get_imethod(call) != IPC_M_SHARE_IN)
1222 return false;
1223
1224 *size = (size_t) ipc_get_arg1(call);
1225 return true;
1226}
1227
1228/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1229 *
1230 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
1231 * calls so that the user doesn't have to remember the meaning of each IPC
1232 * argument.
1233 *
1234 * @param call IPC_M_SHARE_IN call to answer.
1235 * @param src Source address space base.
1236 * @param flags Flags to be used for sharing. Bits can be only cleared.
1237 *
1238 * @return Zero on success or a value from @ref errno.h on failure.
1239 *
1240 */
1241errno_t async_share_in_finalize(ipc_call_t *call, void *src, unsigned int flags)
1242{
1243 assert(call);
1244
1245 cap_call_handle_t chandle = call->cap_handle;
1246 assert(chandle != CAP_NIL);
1247 call->cap_handle = CAP_NIL;
1248
1249 return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) flags);
1250}
1251
1252/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1253 *
1254 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
1255 * calls so that the user doesn't have to remember the meaning of each IPC
1256 * argument.
1257 *
1258 * So far, this wrapper is to be used from within a connection fibril.
1259 *
1260 * @param call Storage for the data of the IPC_M_SHARE_OUT call.
1261 * @param size Storage for the source address space area size.
1262 * @param flags Storage for the sharing flags.
1263 *
1264 * @return True on success, false on failure.
1265 *
1266 */
1267bool async_share_out_receive(ipc_call_t *call, size_t *size,
1268 unsigned int *flags)
1269{
1270 assert(call);
1271 assert(size);
1272 assert(flags);
1273
1274 async_get_call(call);
1275
1276 if (ipc_get_imethod(call) != IPC_M_SHARE_OUT)
1277 return false;
1278
1279 *size = (size_t) ipc_get_arg2(call);
1280 *flags = (unsigned int) ipc_get_arg3(call);
1281 return true;
1282}
1283
1284/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1285 *
1286 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
1287 * calls so that the user doesn't have to remember the meaning of each IPC
1288 * argument.
1289 *
1290 * @param call IPC_M_SHARE_OUT call to answer.
1291 * @param dst Address of the storage for the destination address space area
1292 * base address.
1293 *
1294 * @return Zero on success or a value from @ref errno.h on failure.
1295 *
1296 */
1297errno_t async_share_out_finalize(ipc_call_t *call, void **dst)
1298{
1299 assert(call);
1300
1301 cap_call_handle_t chandle = call->cap_handle;
1302 assert(chandle != CAP_NIL);
1303 call->cap_handle = CAP_NIL;
1304
1305 return ipc_answer_2(chandle, EOK, (sysarg_t) __progsymbols.end,
1306 (sysarg_t) dst);
1307}
1308
1309/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1310 *
1311 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
1312 * calls so that the user doesn't have to remember the meaning of each IPC
1313 * argument.
1314 *
1315 * So far, this wrapper is to be used from within a connection fibril.
1316 *
1317 * @param call Storage for the data of the IPC_M_DATA_READ.
1318 * @param size Storage for the maximum size. Can be NULL.
1319 *
1320 * @return True on success, false on failure.
1321 *
1322 */
1323bool async_data_read_receive(ipc_call_t *call, size_t *size)
1324{
1325 assert(call);
1326
1327 async_get_call(call);
1328
1329 if (ipc_get_imethod(call) != IPC_M_DATA_READ)
1330 return false;
1331
1332 if (size)
1333 *size = (size_t) ipc_get_arg2(call);
1334
1335 return true;
1336}
1337
1338/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
1339 *
1340 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
1341 * calls so that the user doesn't have to remember the meaning of each IPC
1342 * argument.
1343 *
1344 * @param call IPC_M_DATA_READ call to answer.
1345 * @param src Source address for the IPC_M_DATA_READ call.
1346 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
1347 * the maximum size announced by the sender.
1348 *
1349 * @return Zero on success or a value from @ref errno.h on failure.
1350 *
1351 */
1352errno_t async_data_read_finalize(ipc_call_t *call, const void *src, size_t size)
1353{
1354 assert(call);
1355
1356 cap_call_handle_t chandle = call->cap_handle;
1357 assert(chandle != CAP_NIL);
1358 call->cap_handle = CAP_NIL;
1359
1360 return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
1361}
1362
1363/** Wrapper for forwarding any read request
1364 *
1365 */
1366static errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
1367 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1368 ipc_call_t *dataptr)
1369{
1370 if (exch == NULL)
1371 return ENOENT;
1372
1373 ipc_call_t call;
1374 if (!async_data_read_receive(&call, NULL)) {
1375 async_answer_0(&call, EINVAL);
1376 return EINVAL;
1377 }
1378
1379 aid_t msg = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
1380 dataptr);
1381 if (msg == 0) {
1382 async_answer_0(&call, EINVAL);
1383 return EINVAL;
1384 }
1385
1386 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
1387 IPC_FF_ROUTE_FROM_ME);
1388 if (retval != EOK) {
1389 async_forget(msg);
1390 async_answer_0(&call, retval);
1391 return retval;
1392 }
1393
1394 errno_t rc;
1395 async_wait_for(msg, &rc);
1396
1397 return rc;
1398}
1399
1400errno_t async_data_read_forward_0_0(async_exch_t *exch, sysarg_t imethod)
1401{
1402 return async_data_read_forward_fast(exch, imethod, 0, 0, 0, 0, NULL);
1403}
1404
1405errno_t async_data_read_forward_1_0(async_exch_t *exch, sysarg_t imethod,
1406 sysarg_t arg1)
1407{
1408 return async_data_read_forward_fast(exch, imethod, arg1, 0, 0, 0, NULL);
1409}
1410
1411errno_t async_data_read_forward_2_0(async_exch_t *exch, sysarg_t imethod,
1412 sysarg_t arg1, sysarg_t arg2)
1413{
1414 return async_data_read_forward_fast(exch, imethod, arg1, arg2, 0,
1415 0, NULL);
1416}
1417
1418errno_t async_data_read_forward_3_0(async_exch_t *exch, sysarg_t imethod,
1419 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
1420{
1421 return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
1422 0, NULL);
1423}
1424
1425errno_t async_data_read_forward_4_0(async_exch_t *exch, sysarg_t imethod,
1426 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1427{
1428 return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
1429 arg4, NULL);
1430}
1431
1432errno_t async_data_read_forward_0_1(async_exch_t *exch, sysarg_t imethod,
1433 ipc_call_t *dataptr)
1434{
1435 return async_data_read_forward_fast(exch, imethod, 0, 0, 0,
1436 0, dataptr);
1437}
1438
1439errno_t async_data_read_forward_1_1(async_exch_t *exch, sysarg_t imethod,
1440 sysarg_t arg1, ipc_call_t *dataptr)
1441{
1442 return async_data_read_forward_fast(exch, imethod, arg1, 0, 0,
1443 0, dataptr);
1444}
1445
1446errno_t async_data_read_forward_2_1(async_exch_t *exch, sysarg_t imethod,
1447 sysarg_t arg1, sysarg_t arg2, ipc_call_t *dataptr)
1448{
1449 return async_data_read_forward_fast(exch, imethod, arg1, arg2, 0,
1450 0, dataptr);
1451}
1452
1453errno_t async_data_read_forward_3_1(async_exch_t *exch, sysarg_t imethod,
1454 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, ipc_call_t *dataptr)
1455{
1456 return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
1457 0, dataptr);
1458}
1459
1460errno_t async_data_read_forward_4_1(async_exch_t *exch, sysarg_t imethod,
1461 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1462 ipc_call_t *dataptr)
1463{
1464 return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
1465 arg4, dataptr);
1466}
1467
1468/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1469 *
1470 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
1471 * calls so that the user doesn't have to remember the meaning of each IPC
1472 * argument.
1473 *
1474 * So far, this wrapper is to be used from within a connection fibril.
1475 *
1476 * @param call Storage for the data of the IPC_M_DATA_WRITE.
1477 * @param size Storage for the suggested size. May be NULL.
1478 *
1479 * @return True on success, false on failure.
1480 *
1481 */
1482bool async_data_write_receive(ipc_call_t *call, size_t *size)
1483{
1484 assert(call);
1485
1486 async_get_call(call);
1487
1488 if (ipc_get_imethod(call) != IPC_M_DATA_WRITE)
1489 return false;
1490
1491 if (size)
1492 *size = (size_t) ipc_get_arg2(call);
1493
1494 return true;
1495}
1496
1497/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
1498 *
1499 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
1500 * calls so that the user doesn't have to remember the meaning of each IPC
1501 * argument.
1502 *
1503 * @param call IPC_M_DATA_WRITE call to answer.
1504 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
1505 * @param size Final size for the IPC_M_DATA_WRITE call.
1506 *
1507 * @return Zero on success or a value from @ref errno.h on failure.
1508 *
1509 */
1510errno_t async_data_write_finalize(ipc_call_t *call, void *dst, size_t size)
1511{
1512 assert(call);
1513
1514 return async_answer_2(call, EOK, (sysarg_t) dst, (sysarg_t) size);
1515}
1516
1517/** Wrapper for receiving binary data or strings
1518 *
1519 * This wrapper only makes it more comfortable to use async_data_write_*
1520 * functions to receive binary data or strings.
1521 *
1522 * @param data Pointer to data pointer (which should be later disposed
1523 * by free()). If the operation fails, the pointer is not
1524 * touched.
1525 * @param nullterm If true then the received data is always zero terminated.
1526 * This also causes to allocate one extra byte beyond the
1527 * raw transmitted data.
1528 * @param min_size Minimum size (in bytes) of the data to receive.
1529 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
1530 * no limit.
1531 * @param granulariy If non-zero then the size of the received data has to
1532 * be divisible by this value.
1533 * @param received If not NULL, the size of the received data is stored here.
1534 *
1535 * @return Zero on success or a value from @ref errno.h on failure.
1536 *
1537 */
1538errno_t async_data_write_accept(void **data, const bool nullterm,
1539 const size_t min_size, const size_t max_size, const size_t granularity,
1540 size_t *received)
1541{
1542 assert(data);
1543
1544 ipc_call_t call;
1545 size_t size;
1546 if (!async_data_write_receive(&call, &size)) {
1547 async_answer_0(&call, EINVAL);
1548 return EINVAL;
1549 }
1550
1551 if (size < min_size) {
1552 async_answer_0(&call, EINVAL);
1553 return EINVAL;
1554 }
1555
1556 if ((max_size > 0) && (size > max_size)) {
1557 async_answer_0(&call, EINVAL);
1558 return EINVAL;
1559 }
1560
1561 if ((granularity > 0) && ((size % granularity) != 0)) {
1562 async_answer_0(&call, EINVAL);
1563 return EINVAL;
1564 }
1565
1566 void *arg_data;
1567
1568 if (nullterm)
1569 arg_data = malloc(size + 1);
1570 else
1571 arg_data = malloc(size);
1572
1573 if (arg_data == NULL) {
1574 async_answer_0(&call, ENOMEM);
1575 return ENOMEM;
1576 }
1577
1578 errno_t rc = async_data_write_finalize(&call, arg_data, size);
1579 if (rc != EOK) {
1580 free(arg_data);
1581 return rc;
1582 }
1583
1584 if (nullterm)
1585 ((char *) arg_data)[size] = 0;
1586
1587 *data = arg_data;
1588 if (received != NULL)
1589 *received = size;
1590
1591 return EOK;
1592}
1593
1594/** Wrapper for voiding any data that is about to be received
1595 *
1596 * This wrapper can be used to void any pending data
1597 *
1598 * @param retval Error value from @ref errno.h to be returned to the caller.
1599 *
1600 */
1601void async_data_write_void(errno_t retval)
1602{
1603 ipc_call_t call;
1604 async_data_write_receive(&call, NULL);
1605 async_answer_0(&call, retval);
1606}
1607
1608/** Wrapper for forwarding any data that is about to be received
1609 *
1610 */
1611static errno_t async_data_write_forward_fast(async_exch_t *exch,
1612 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1613 sysarg_t arg4, ipc_call_t *dataptr)
1614{
1615 if (exch == NULL)
1616 return ENOENT;
1617
1618 ipc_call_t call;
1619 if (!async_data_write_receive(&call, NULL)) {
1620 async_answer_0(&call, EINVAL);
1621 return EINVAL;
1622 }
1623
1624 aid_t msg = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
1625 dataptr);
1626 if (msg == 0) {
1627 async_answer_0(&call, EINVAL);
1628 return EINVAL;
1629 }
1630
1631 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
1632 IPC_FF_ROUTE_FROM_ME);
1633 if (retval != EOK) {
1634 async_forget(msg);
1635 async_answer_0(&call, retval);
1636 return retval;
1637 }
1638
1639 errno_t rc;
1640 async_wait_for(msg, &rc);
1641
1642 return rc;
1643}
1644
1645errno_t async_data_write_forward_0_0(async_exch_t *exch, sysarg_t imethod)
1646{
1647 return async_data_write_forward_fast(exch, imethod, 0, 0, 0,
1648 0, NULL);
1649}
1650
1651errno_t async_data_write_forward_1_0(async_exch_t *exch, sysarg_t imethod,
1652 sysarg_t arg1)
1653{
1654 return async_data_write_forward_fast(exch, imethod, arg1, 0, 0,
1655 0, NULL);
1656}
1657
1658errno_t async_data_write_forward_2_0(async_exch_t *exch, sysarg_t imethod,
1659 sysarg_t arg1, sysarg_t arg2)
1660{
1661 return async_data_write_forward_fast(exch, imethod, arg1, arg2, 0,
1662 0, NULL);
1663}
1664
1665errno_t async_data_write_forward_3_0(async_exch_t *exch, sysarg_t imethod,
1666 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
1667{
1668 return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
1669 0, NULL);
1670}
1671
1672errno_t async_data_write_forward_4_0(async_exch_t *exch, sysarg_t imethod,
1673 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1674{
1675 return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
1676 arg4, NULL);
1677}
1678
1679errno_t async_data_write_forward_0_1(async_exch_t *exch, sysarg_t imethod,
1680 ipc_call_t *dataptr)
1681{
1682 return async_data_write_forward_fast(exch, imethod, 0, 0, 0,
1683 0, dataptr);
1684}
1685
1686errno_t async_data_write_forward_1_1(async_exch_t *exch, sysarg_t imethod,
1687 sysarg_t arg1, ipc_call_t *dataptr)
1688{
1689 return async_data_write_forward_fast(exch, imethod, arg1, 0, 0,
1690 0, dataptr);
1691}
1692
1693errno_t async_data_write_forward_2_1(async_exch_t *exch, sysarg_t imethod,
1694 sysarg_t arg1, sysarg_t arg2, ipc_call_t *dataptr)
1695{
1696 return async_data_write_forward_fast(exch, imethod, arg1, arg2, 0,
1697 0, dataptr);
1698}
1699
1700errno_t async_data_write_forward_3_1(async_exch_t *exch, sysarg_t imethod,
1701 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, ipc_call_t *dataptr)
1702{
1703 return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
1704 0, dataptr);
1705}
1706
1707errno_t async_data_write_forward_4_1(async_exch_t *exch, sysarg_t imethod,
1708 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1709 ipc_call_t *dataptr)
1710{
1711 return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
1712 arg4, dataptr);
1713}
1714
1715/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1716 *
1717 * If the current call is IPC_M_CONNECT_TO_ME then a new
1718 * async session is created for the accepted phone.
1719 *
1720 * @param mgmt Exchange management style.
1721 *
1722 * @return New async session.
1723 * @return NULL on failure.
1724 *
1725 */
1726async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
1727{
1728 /* Accept the phone */
1729 ipc_call_t call;
1730 async_get_call(&call);
1731
1732 cap_phone_handle_t phandle = (cap_handle_t) ipc_get_arg5(&call);
1733
1734 if ((ipc_get_imethod(&call) != IPC_M_CONNECT_TO_ME) ||
1735 !cap_handle_valid((phandle))) {
1736 async_answer_0(&call, EINVAL);
1737 return NULL;
1738 }
1739
1740 async_sess_t *sess = create_session(phandle, mgmt, 0, 0, 0);
1741 if (sess == NULL) {
1742 ipc_hangup(phandle);
1743 async_answer_0(&call, errno);
1744 } else {
1745 /* Acknowledge the connected phone */
1746 async_answer_0(&call, EOK);
1747 }
1748
1749 return sess;
1750}
1751
1752/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1753 *
1754 * If the call is IPC_M_CONNECT_TO_ME then a new
1755 * async session is created. However, the phone is
1756 * not accepted automatically.
1757 *
1758 * @param mgmt Exchange management style.
1759 * @param call Call data.
1760 *
1761 * @return New async session.
1762 * @return NULL on failure.
1763 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
1764 *
1765 */
1766async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
1767{
1768 cap_phone_handle_t phandle = (cap_handle_t) ipc_get_arg5(call);
1769
1770 if ((ipc_get_imethod(call) != IPC_M_CONNECT_TO_ME) ||
1771 !cap_handle_valid((phandle)))
1772 return NULL;
1773
1774 return create_session(phandle, mgmt, 0, 0, 0);
1775}
1776
1777bool async_state_change_receive(ipc_call_t *call)
1778{
1779 assert(call);
1780
1781 async_get_call(call);
1782
1783 if (ipc_get_imethod(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
1784 return false;
1785
1786 return true;
1787}
1788
1789errno_t async_state_change_finalize(ipc_call_t *call, async_exch_t *other_exch)
1790{
1791 assert(call);
1792
1793 return async_answer_1(call, EOK, cap_handle_raw(other_exch->phone));
1794}
1795
1796__noreturn void async_manager(void)
1797{
1798 fibril_event_t ever = FIBRIL_EVENT_INIT;
1799 fibril_wait_for(&ever);
1800 __builtin_unreachable();
1801}
1802
1803/** @}
1804 */
Note: See TracBrowser for help on using the repository browser.