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

Last change on this file since 102f641 was 102f641, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

Correcting syntax according to ccheck

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