source: mainline/uspace/lib/c/generic/async.c@ 3c22f70

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

Pass client task hash into async_new_connection().

  • Property mode set to 100644
File size: 41.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 synchronization problems.
44 *
45 * Default semantics:
46 * - async_send_*(): Send asynchronously. If the kernel refuses to send
47 * more messages, [ try to get responses from kernel, if
48 * nothing found, might try synchronous ]
49 *
50 * Example of use (pseudo C):
51 *
52 * 1) Multithreaded client application
53 *
54 * fibril_create(fibril1, ...);
55 * fibril_create(fibril2, ...);
56 * ...
57 *
58 * int fibril1(void *arg)
59 * {
60 * conn = ipc_connect_me_to();
61 * c1 = async_send(conn);
62 * c2 = async_send(conn);
63 * async_wait_for(c1);
64 * async_wait_for(c2);
65 * ...
66 * }
67 *
68 *
69 * 2) Multithreaded server application
70 *
71 * main()
72 * {
73 * async_manager();
74 * }
75 *
76 * my_client_connection(icallid, *icall)
77 * {
78 * if (want_refuse) {
79 * ipc_answer_0(icallid, ELIMIT);
80 * return;
81 * }
82 * ipc_answer_0(icallid, EOK);
83 *
84 * callid = async_get_call(&call);
85 * somehow_handle_the_call(callid, call);
86 * ipc_answer_2(callid, 1, 2, 3);
87 *
88 * callid = async_get_call(&call);
89 * ...
90 * }
91 *
92 */
93
94#include <futex.h>
95#include <async.h>
96#include <async_priv.h>
97#include <fibril.h>
98#include <stdio.h>
99#include <adt/hash_table.h>
100#include <adt/list.h>
101#include <ipc/ipc.h>
102#include <assert.h>
103#include <errno.h>
104#include <sys/time.h>
105#include <arch/barrier.h>
106#include <bool.h>
107
108atomic_t async_futex = FUTEX_INITIALIZER;
109
110/** Number of threads waiting for IPC in the kernel. */
111atomic_t threads_in_ipc_wait = { 0 };
112
113typedef struct {
114 awaiter_t wdata;
115
116 /** If reply was received. */
117 bool done;
118
119 /** Pointer to where the answer data is stored. */
120 ipc_call_t *dataptr;
121
122 sysarg_t retval;
123} amsg_t;
124
125/**
126 * Structures of this type are used to group information about a call and a
127 * message queue link.
128 */
129typedef struct {
130 link_t link;
131 ipc_callid_t callid;
132 ipc_call_t call;
133} msg_t;
134
135typedef struct {
136 awaiter_t wdata;
137
138 /** Hash table link. */
139 link_t link;
140
141 /** Incoming client task hash. */
142 sysarg_t in_task_hash;
143 /** Incoming phone hash. */
144 sysarg_t in_phone_hash;
145
146 /** Messages that should be delivered to this fibril. */
147 link_t msg_queue;
148
149 /** Identification of the opening call. */
150 ipc_callid_t callid;
151 /** Call data of the opening call. */
152 ipc_call_t call;
153
154 /** Identification of the closing call. */
155 ipc_callid_t close_callid;
156
157 /** Fibril function that will be used to handle the connection. */
158 void (*cfibril)(ipc_callid_t, ipc_call_t *);
159} connection_t;
160
161/** Identifier of the incoming connection handled by the current fibril. */
162fibril_local connection_t *FIBRIL_connection;
163
164static void default_client_connection(ipc_callid_t callid, ipc_call_t *call);
165static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call);
166
167/**
168 * Pointer to a fibril function that will be used to handle connections.
169 */
170static async_client_conn_t client_connection = default_client_connection;
171
172/**
173 * Pointer to a fibril function that will be used to handle interrupt
174 * notifications.
175 */
176static async_client_conn_t interrupt_received = default_interrupt_received;
177
178static hash_table_t conn_hash_table;
179static LIST_INITIALIZE(timeout_list);
180
181#define CONN_HASH_TABLE_CHAINS 32
182
183/** Compute hash into the connection hash table based on the source phone hash.
184 *
185 * @param key Pointer to source phone hash.
186 *
187 * @return Index into the connection hash table.
188 *
189 */
190static hash_index_t conn_hash(unsigned long *key)
191{
192 assert(key);
193 return (((*key) >> 4) % CONN_HASH_TABLE_CHAINS);
194}
195
196/** Compare hash table item with a key.
197 *
198 * @param key Array containing the source phone hash as the only item.
199 * @param keys Expected 1 but ignored.
200 * @param item Connection hash table item.
201 *
202 * @return True on match, false otherwise.
203 *
204 */
205static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
206{
207 connection_t *hs = hash_table_get_instance(item, connection_t, link);
208 return (key[0] == hs->in_phone_hash);
209}
210
211/** Connection hash table removal callback function.
212 *
213 * This function is called whenever a connection is removed from the connection
214 * hash table.
215 *
216 * @param item Connection hash table item being removed.
217 *
218 */
219static void conn_remove(link_t *item)
220{
221 free(hash_table_get_instance(item, connection_t, link));
222}
223
224
225/** Operations for the connection hash table. */
226static hash_table_operations_t conn_hash_table_ops = {
227 .hash = conn_hash,
228 .compare = conn_compare,
229 .remove_callback = conn_remove
230};
231
232/** Sort in current fibril's timeout request.
233 *
234 * @param wd Wait data of the current fibril.
235 *
236 */
237void async_insert_timeout(awaiter_t *wd)
238{
239 wd->to_event.occurred = false;
240 wd->to_event.inlist = true;
241
242 link_t *tmp = timeout_list.next;
243 while (tmp != &timeout_list) {
244 awaiter_t *cur;
245
246 cur = list_get_instance(tmp, awaiter_t, to_event.link);
247 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
248 break;
249 tmp = tmp->next;
250 }
251
252 list_append(&wd->to_event.link, tmp);
253}
254
255/** Try to route a call to an appropriate connection fibril.
256 *
257 * If the proper connection fibril is found, a message with the call is added to
258 * its message queue. If the fibril was not active, it is activated and all
259 * timeouts are unregistered.
260 *
261 * @param callid Hash of the incoming call.
262 * @param call Data of the incoming call.
263 *
264 * @return False if the call doesn't match any connection.
265 * True if the call was passed to the respective connection fibril.
266 *
267 */
268static bool route_call(ipc_callid_t callid, ipc_call_t *call)
269{
270 futex_down(&async_futex);
271
272 unsigned long key = call->in_phone_hash;
273 link_t *hlp = hash_table_find(&conn_hash_table, &key);
274
275 if (!hlp) {
276 futex_up(&async_futex);
277 return false;
278 }
279
280 connection_t *conn = hash_table_get_instance(hlp, connection_t, link);
281
282 msg_t *msg = malloc(sizeof(*msg));
283 if (!msg) {
284 futex_up(&async_futex);
285 return false;
286 }
287
288 msg->callid = callid;
289 msg->call = *call;
290 list_append(&msg->link, &conn->msg_queue);
291
292 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
293 conn->close_callid = callid;
294
295 /* If the connection fibril is waiting for an event, activate it */
296 if (!conn->wdata.active) {
297
298 /* If in timeout list, remove it */
299 if (conn->wdata.to_event.inlist) {
300 conn->wdata.to_event.inlist = false;
301 list_remove(&conn->wdata.to_event.link);
302 }
303
304 conn->wdata.active = true;
305 fibril_add_ready(conn->wdata.fid);
306 }
307
308 futex_up(&async_futex);
309 return true;
310}
311
312/** Notification fibril.
313 *
314 * When a notification arrives, a fibril with this implementing function is
315 * created. It calls interrupt_received() and does the final cleanup.
316 *
317 * @param arg Message structure pointer.
318 *
319 * @return Always zero.
320 *
321 */
322static int notification_fibril(void *arg)
323{
324 msg_t *msg = (msg_t *) arg;
325 interrupt_received(msg->callid, &msg->call);
326
327 free(msg);
328 return 0;
329}
330
331/** Process interrupt notification.
332 *
333 * A new fibril is created which would process the notification.
334 *
335 * @param callid Hash of the incoming call.
336 * @param call Data of the incoming call.
337 *
338 * @return False if an error occured.
339 * True if the call was passed to the notification fibril.
340 *
341 */
342static bool process_notification(ipc_callid_t callid, ipc_call_t *call)
343{
344 futex_down(&async_futex);
345
346 msg_t *msg = malloc(sizeof(*msg));
347 if (!msg) {
348 futex_up(&async_futex);
349 return false;
350 }
351
352 msg->callid = callid;
353 msg->call = *call;
354
355 fid_t fid = fibril_create(notification_fibril, msg);
356 fibril_add_ready(fid);
357
358 futex_up(&async_futex);
359 return true;
360}
361
362/** Return new incoming message for the current (fibril-local) connection.
363 *
364 * @param call Storage where the incoming call data will be stored.
365 * @param usecs Timeout in microseconds. Zero denotes no timeout.
366 *
367 * @return If no timeout was specified, then a hash of the
368 * incoming call is returned. If a timeout is specified,
369 * then a hash of the incoming call is returned unless
370 * the timeout expires prior to receiving a message. In
371 * that case zero is returned.
372 *
373 */
374ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
375{
376 assert(FIBRIL_connection);
377
378 /* Why doing this?
379 * GCC 4.1.0 coughs on FIBRIL_connection-> dereference.
380 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
381 * I would never expect to find so many errors in
382 * a compiler.
383 */
384 connection_t *conn = FIBRIL_connection;
385
386 futex_down(&async_futex);
387
388 if (usecs) {
389 gettimeofday(&conn->wdata.to_event.expires, NULL);
390 tv_add(&conn->wdata.to_event.expires, usecs);
391 } else
392 conn->wdata.to_event.inlist = false;
393
394 /* If nothing in queue, wait until something arrives */
395 while (list_empty(&conn->msg_queue)) {
396 if (conn->close_callid) {
397 /*
398 * Handle the case when the connection was already
399 * closed by the client but the server did not notice
400 * the first IPC_M_PHONE_HUNGUP call and continues to
401 * call async_get_call_timeout(). Repeat
402 * IPC_M_PHONE_HUNGUP until the caller notices.
403 */
404 memset(call, 0, sizeof(ipc_call_t));
405 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
406 futex_up(&async_futex);
407 return conn->close_callid;
408 }
409
410 if (usecs)
411 async_insert_timeout(&conn->wdata);
412
413 conn->wdata.active = false;
414
415 /*
416 * Note: the current fibril will be rescheduled either due to a
417 * timeout or due to an arriving message destined to it. In the
418 * former case, handle_expired_timeouts() and, in the latter
419 * case, route_call() will perform the wakeup.
420 */
421 fibril_switch(FIBRIL_TO_MANAGER);
422
423 /*
424 * Futex is up after getting back from async_manager.
425 * Get it again.
426 */
427 futex_down(&async_futex);
428 if ((usecs) && (conn->wdata.to_event.occurred)
429 && (list_empty(&conn->msg_queue))) {
430 /* If we timed out -> exit */
431 futex_up(&async_futex);
432 return 0;
433 }
434 }
435
436 msg_t *msg = list_get_instance(conn->msg_queue.next, msg_t, link);
437 list_remove(&msg->link);
438
439 ipc_callid_t callid = msg->callid;
440 *call = msg->call;
441 free(msg);
442
443 futex_up(&async_futex);
444 return callid;
445}
446
447/** Default fibril function that gets called to handle new connection.
448 *
449 * This function is defined as a weak symbol - to be redefined in user code.
450 *
451 * @param callid Hash of the incoming call.
452 * @param call Data of the incoming call.
453 *
454 */
455static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
456{
457 ipc_answer_0(callid, ENOENT);
458}
459
460/** Default fibril function that gets called to handle interrupt notifications.
461 *
462 * This function is defined as a weak symbol - to be redefined in user code.
463 *
464 * @param callid Hash of the incoming call.
465 * @param call Data of the incoming call.
466 *
467 */
468static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
469{
470}
471
472/** Wrapper for client connection fibril.
473 *
474 * When a new connection arrives, a fibril with this implementing function is
475 * created. It calls client_connection() and does the final cleanup.
476 *
477 * @param arg Connection structure pointer.
478 *
479 * @return Always zero.
480 *
481 */
482static int connection_fibril(void *arg)
483{
484 /*
485 * Setup fibril-local connection pointer and call client_connection().
486 *
487 */
488 FIBRIL_connection = (connection_t *) arg;
489 FIBRIL_connection->cfibril(FIBRIL_connection->callid,
490 &FIBRIL_connection->call);
491
492 /* Remove myself from the connection hash table */
493 futex_down(&async_futex);
494 unsigned long key = FIBRIL_connection->in_phone_hash;
495 hash_table_remove(&conn_hash_table, &key, 1);
496 futex_up(&async_futex);
497
498 /* Answer all remaining messages with EHANGUP */
499 while (!list_empty(&FIBRIL_connection->msg_queue)) {
500 msg_t *msg;
501
502 msg = list_get_instance(FIBRIL_connection->msg_queue.next,
503 msg_t, link);
504 list_remove(&msg->link);
505 ipc_answer_0(msg->callid, EHANGUP);
506 free(msg);
507 }
508
509 if (FIBRIL_connection->close_callid)
510 ipc_answer_0(FIBRIL_connection->close_callid, EOK);
511
512 return 0;
513}
514
515/** Create a new fibril for a new connection.
516 *
517 * Create new fibril for connection, fill in connection structures and inserts
518 * it into the hash table, so that later we can easily do routing of messages to
519 * particular fibrils.
520 *
521 * @param in_task_hash Identification of the incoming connection.
522 * @param in_phone_hash Identification of the incoming connection.
523 * @param callid Hash of the opening IPC_M_CONNECT_ME_TO call.
524 * If callid is zero, the connection was opened by
525 * accepting the IPC_M_CONNECT_TO_ME call and this function
526 * is called directly by the server.
527 * @param call Call data of the opening call.
528 * @param cfibril Fibril function that should be called upon opening the
529 * connection.
530 *
531 * @return New fibril id or NULL on failure.
532 *
533 */
534fid_t async_new_connection(sysarg_t in_task_hash, sysarg_t in_phone_hash,
535 ipc_callid_t callid, ipc_call_t *call,
536 void (*cfibril)(ipc_callid_t, ipc_call_t *))
537{
538 connection_t *conn = malloc(sizeof(*conn));
539 if (!conn) {
540 if (callid)
541 ipc_answer_0(callid, ENOMEM);
542 return (uintptr_t) NULL;
543 }
544
545 conn->in_task_hash = in_task_hash;
546 conn->in_phone_hash = in_phone_hash;
547 list_initialize(&conn->msg_queue);
548 conn->callid = callid;
549 conn->close_callid = 0;
550
551 if (call)
552 conn->call = *call;
553
554 /* We will activate the fibril ASAP */
555 conn->wdata.active = true;
556 conn->cfibril = cfibril;
557 conn->wdata.fid = fibril_create(connection_fibril, conn);
558
559 if (!conn->wdata.fid) {
560 free(conn);
561 if (callid)
562 ipc_answer_0(callid, ENOMEM);
563 return (uintptr_t) NULL;
564 }
565
566 /* Add connection to the connection hash table */
567 unsigned long key = conn->in_phone_hash;
568
569 futex_down(&async_futex);
570 hash_table_insert(&conn_hash_table, &key, &conn->link);
571 futex_up(&async_futex);
572
573 fibril_add_ready(conn->wdata.fid);
574
575 return conn->wdata.fid;
576}
577
578/** Handle a call that was received.
579 *
580 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
581 * Otherwise the call is routed to its connection fibril.
582 *
583 * @param callid Hash of the incoming call.
584 * @param call Data of the incoming call.
585 *
586 */
587static void handle_call(ipc_callid_t callid, ipc_call_t *call)
588{
589 /* Unrouted call - do some default behaviour */
590 if ((callid & IPC_CALLID_NOTIFICATION)) {
591 process_notification(callid, call);
592 goto out;
593 }
594
595 switch (IPC_GET_IMETHOD(*call)) {
596 case IPC_M_CONNECT_ME:
597 case IPC_M_CONNECT_ME_TO:
598 /* Open new connection with fibril etc. */
599 async_new_connection(call->in_task_hash, IPC_GET_ARG5(*call),
600 callid, call, client_connection);
601 goto out;
602 }
603
604 /* Try to route the call through the connection hash table */
605 if (route_call(callid, call))
606 goto out;
607
608 /* Unknown call from unknown phone - hang it up */
609 ipc_answer_0(callid, EHANGUP);
610 return;
611
612out:
613 ;
614}
615
616/** Fire all timeouts that expired. */
617static void handle_expired_timeouts(void)
618{
619 struct timeval tv;
620 gettimeofday(&tv, NULL);
621
622 futex_down(&async_futex);
623
624 link_t *cur = timeout_list.next;
625 while (cur != &timeout_list) {
626 awaiter_t *waiter;
627
628 waiter = list_get_instance(cur, awaiter_t, to_event.link);
629 if (tv_gt(&waiter->to_event.expires, &tv))
630 break;
631
632 cur = cur->next;
633
634 list_remove(&waiter->to_event.link);
635 waiter->to_event.inlist = false;
636 waiter->to_event.occurred = true;
637
638 /*
639 * Redundant condition?
640 * The fibril should not be active when it gets here.
641 */
642 if (!waiter->active) {
643 waiter->active = true;
644 fibril_add_ready(waiter->fid);
645 }
646 }
647
648 futex_up(&async_futex);
649}
650
651/** Endless loop dispatching incoming calls and answers.
652 *
653 * @return Never returns.
654 *
655 */
656static int async_manager_worker(void)
657{
658 while (true) {
659 if (fibril_switch(FIBRIL_FROM_MANAGER)) {
660 futex_up(&async_futex);
661 /*
662 * async_futex is always held when entering a manager
663 * fibril.
664 */
665 continue;
666 }
667
668 futex_down(&async_futex);
669
670 suseconds_t timeout;
671 if (!list_empty(&timeout_list)) {
672 awaiter_t *waiter = list_get_instance(timeout_list.next,
673 awaiter_t, to_event.link);
674
675 struct timeval tv;
676 gettimeofday(&tv, NULL);
677
678 if (tv_gteq(&tv, &waiter->to_event.expires)) {
679 futex_up(&async_futex);
680 handle_expired_timeouts();
681 continue;
682 } else
683 timeout = tv_sub(&waiter->to_event.expires,
684 &tv);
685 } else
686 timeout = SYNCH_NO_TIMEOUT;
687
688 futex_up(&async_futex);
689
690 atomic_inc(&threads_in_ipc_wait);
691
692 ipc_call_t call;
693 ipc_callid_t callid = ipc_wait_cycle(&call, timeout,
694 SYNCH_FLAGS_NONE);
695
696 atomic_dec(&threads_in_ipc_wait);
697
698 if (!callid) {
699 handle_expired_timeouts();
700 continue;
701 }
702
703 if (callid & IPC_CALLID_ANSWERED)
704 continue;
705
706 handle_call(callid, &call);
707 }
708
709 return 0;
710}
711
712/** Function to start async_manager as a standalone fibril.
713 *
714 * When more kernel threads are used, one async manager should exist per thread.
715 *
716 * @param arg Unused.
717 * @return Never returns.
718 *
719 */
720static int async_manager_fibril(void *arg)
721{
722 futex_up(&async_futex);
723
724 /*
725 * async_futex is always locked when entering manager
726 */
727 async_manager_worker();
728
729 return 0;
730}
731
732/** Add one manager to manager list. */
733void async_create_manager(void)
734{
735 fid_t fid = fibril_create(async_manager_fibril, NULL);
736 fibril_add_manager(fid);
737}
738
739/** Remove one manager from manager list */
740void async_destroy_manager(void)
741{
742 fibril_remove_manager();
743}
744
745/** Initialize the async framework.
746 *
747 * @return Zero on success or an error code.
748 */
749int __async_init(void)
750{
751 if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
752 &conn_hash_table_ops)) {
753 printf("%s: Cannot create async hash table\n", "libc");
754 return ENOMEM;
755 }
756
757 _async_sess_init();
758
759 return 0;
760}
761
762/** Reply received callback.
763 *
764 * This function is called whenever a reply for an asynchronous message sent out
765 * by the asynchronous framework is received.
766 *
767 * Notify the fibril which is waiting for this message that it has arrived.
768 *
769 * @param arg Pointer to the asynchronous message record.
770 * @param retval Value returned in the answer.
771 * @param data Call data of the answer.
772 */
773static void reply_received(void *arg, int retval, ipc_call_t *data)
774{
775 futex_down(&async_futex);
776
777 amsg_t *msg = (amsg_t *) arg;
778 msg->retval = retval;
779
780 /* Copy data after futex_down, just in case the call was detached */
781 if ((msg->dataptr) && (data))
782 *msg->dataptr = *data;
783
784 write_barrier();
785
786 /* Remove message from timeout list */
787 if (msg->wdata.to_event.inlist)
788 list_remove(&msg->wdata.to_event.link);
789
790 msg->done = true;
791 if (!msg->wdata.active) {
792 msg->wdata.active = true;
793 fibril_add_ready(msg->wdata.fid);
794 }
795
796 futex_up(&async_futex);
797}
798
799/** Send message and return id of the sent message.
800 *
801 * The return value can be used as input for async_wait() to wait for
802 * completion.
803 *
804 * @param phoneid Handle of the phone that will be used for the send.
805 * @param method Service-defined method.
806 * @param arg1 Service-defined payload argument.
807 * @param arg2 Service-defined payload argument.
808 * @param arg3 Service-defined payload argument.
809 * @param arg4 Service-defined payload argument.
810 * @param dataptr If non-NULL, storage where the reply data will be
811 * stored.
812 *
813 * @return Hash of the sent message or 0 on error.
814 *
815 */
816aid_t async_send_fast(int phoneid, sysarg_t method, sysarg_t arg1,
817 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
818{
819 amsg_t *msg = malloc(sizeof(*msg));
820
821 if (!msg)
822 return 0;
823
824 msg->done = false;
825 msg->dataptr = dataptr;
826
827 msg->wdata.to_event.inlist = false;
828 /* We may sleep in the next method, but it will use its own mechanism */
829 msg->wdata.active = true;
830
831 ipc_call_async_4(phoneid, method, arg1, arg2, arg3, arg4, msg,
832 reply_received, true);
833
834 return (aid_t) msg;
835}
836
837/** Send message and return id of the sent message
838 *
839 * The return value can be used as input for async_wait() to wait for
840 * completion.
841 *
842 * @param phoneid Handle of the phone that will be used for the send.
843 * @param method Service-defined method.
844 * @param arg1 Service-defined payload argument.
845 * @param arg2 Service-defined payload argument.
846 * @param arg3 Service-defined payload argument.
847 * @param arg4 Service-defined payload argument.
848 * @param arg5 Service-defined payload argument.
849 * @param dataptr If non-NULL, storage where the reply data will be
850 * stored.
851 *
852 * @return Hash of the sent message or 0 on error.
853 *
854 */
855aid_t async_send_slow(int phoneid, sysarg_t method, sysarg_t arg1,
856 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
857 ipc_call_t *dataptr)
858{
859 amsg_t *msg = malloc(sizeof(*msg));
860
861 if (!msg)
862 return 0;
863
864 msg->done = false;
865 msg->dataptr = dataptr;
866
867 msg->wdata.to_event.inlist = false;
868 /* We may sleep in next method, but it will use its own mechanism */
869 msg->wdata.active = true;
870
871 ipc_call_async_5(phoneid, method, arg1, arg2, arg3, arg4, arg5, msg,
872 reply_received, true);
873
874 return (aid_t) msg;
875}
876
877/** Wait for a message sent by the async framework.
878 *
879 * @param amsgid Hash of the message to wait for.
880 * @param retval Pointer to storage where the retval of the answer will
881 * be stored.
882 *
883 */
884void async_wait_for(aid_t amsgid, sysarg_t *retval)
885{
886 amsg_t *msg = (amsg_t *) amsgid;
887
888 futex_down(&async_futex);
889 if (msg->done) {
890 futex_up(&async_futex);
891 goto done;
892 }
893
894 msg->wdata.fid = fibril_get_id();
895 msg->wdata.active = false;
896 msg->wdata.to_event.inlist = false;
897
898 /* Leave the async_futex locked when entering this function */
899 fibril_switch(FIBRIL_TO_MANAGER);
900
901 /* Futex is up automatically after fibril_switch */
902
903done:
904 if (retval)
905 *retval = msg->retval;
906
907 free(msg);
908}
909
910/** Wait for a message sent by the async framework, timeout variant.
911 *
912 * @param amsgid Hash of the message to wait for.
913 * @param retval Pointer to storage where the retval of the answer will
914 * be stored.
915 * @param timeout Timeout in microseconds.
916 *
917 * @return Zero on success, ETIMEOUT if the timeout has expired.
918 *
919 */
920int async_wait_timeout(aid_t amsgid, sysarg_t *retval, suseconds_t timeout)
921{
922 amsg_t *msg = (amsg_t *) amsgid;
923
924 /* TODO: Let it go through the event read at least once */
925 if (timeout < 0)
926 return ETIMEOUT;
927
928 futex_down(&async_futex);
929 if (msg->done) {
930 futex_up(&async_futex);
931 goto done;
932 }
933
934 gettimeofday(&msg->wdata.to_event.expires, NULL);
935 tv_add(&msg->wdata.to_event.expires, timeout);
936
937 msg->wdata.fid = fibril_get_id();
938 msg->wdata.active = false;
939 async_insert_timeout(&msg->wdata);
940
941 /* Leave the async_futex locked when entering this function */
942 fibril_switch(FIBRIL_TO_MANAGER);
943
944 /* Futex is up automatically after fibril_switch */
945
946 if (!msg->done)
947 return ETIMEOUT;
948
949done:
950 if (retval)
951 *retval = msg->retval;
952
953 free(msg);
954
955 return 0;
956}
957
958/** Wait for specified time.
959 *
960 * The current fibril is suspended but the thread continues to execute.
961 *
962 * @param timeout Duration of the wait in microseconds.
963 *
964 */
965void async_usleep(suseconds_t timeout)
966{
967 amsg_t *msg = malloc(sizeof(*msg));
968
969 if (!msg)
970 return;
971
972 msg->wdata.fid = fibril_get_id();
973 msg->wdata.active = false;
974
975 gettimeofday(&msg->wdata.to_event.expires, NULL);
976 tv_add(&msg->wdata.to_event.expires, timeout);
977
978 futex_down(&async_futex);
979
980 async_insert_timeout(&msg->wdata);
981
982 /* Leave the async_futex locked when entering this function */
983 fibril_switch(FIBRIL_TO_MANAGER);
984
985 /* Futex is up automatically after fibril_switch() */
986
987 free(msg);
988}
989
990/** Setter for client_connection function pointer.
991 *
992 * @param conn Function that will implement a new connection fibril.
993 *
994 */
995void async_set_client_connection(async_client_conn_t conn)
996{
997 client_connection = conn;
998}
999
1000/** Setter for interrupt_received function pointer.
1001 *
1002 * @param intr Function that will implement a new interrupt
1003 * notification fibril.
1004 */
1005void async_set_interrupt_received(async_client_conn_t intr)
1006{
1007 interrupt_received = intr;
1008}
1009
1010/** Pseudo-synchronous message sending - fast version.
1011 *
1012 * Send message asynchronously and return only after the reply arrives.
1013 *
1014 * This function can only transfer 4 register payload arguments. For
1015 * transferring more arguments, see the slower async_req_slow().
1016 *
1017 * @param phoneid Hash of the phone through which to make the call.
1018 * @param method Method of the call.
1019 * @param arg1 Service-defined payload argument.
1020 * @param arg2 Service-defined payload argument.
1021 * @param arg3 Service-defined payload argument.
1022 * @param arg4 Service-defined payload argument.
1023 * @param r1 If non-NULL, storage for the 1st reply argument.
1024 * @param r2 If non-NULL, storage for the 2nd reply argument.
1025 * @param r3 If non-NULL, storage for the 3rd reply argument.
1026 * @param r4 If non-NULL, storage for the 4th reply argument.
1027 * @param r5 If non-NULL, storage for the 5th reply argument.
1028 *
1029 * @return Return code of the reply or a negative error code.
1030 *
1031 */
1032sysarg_t async_req_fast(int phoneid, sysarg_t method, sysarg_t arg1,
1033 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
1034 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
1035{
1036 ipc_call_t result;
1037 aid_t eid = async_send_4(phoneid, method, arg1, arg2, arg3, arg4,
1038 &result);
1039
1040 sysarg_t rc;
1041 async_wait_for(eid, &rc);
1042
1043 if (r1)
1044 *r1 = IPC_GET_ARG1(result);
1045
1046 if (r2)
1047 *r2 = IPC_GET_ARG2(result);
1048
1049 if (r3)
1050 *r3 = IPC_GET_ARG3(result);
1051
1052 if (r4)
1053 *r4 = IPC_GET_ARG4(result);
1054
1055 if (r5)
1056 *r5 = IPC_GET_ARG5(result);
1057
1058 return rc;
1059}
1060
1061/** Pseudo-synchronous message sending - slow version.
1062 *
1063 * Send message asynchronously and return only after the reply arrives.
1064 *
1065 * @param phoneid Hash of the phone through which to make the call.
1066 * @param method Method of the call.
1067 * @param arg1 Service-defined payload argument.
1068 * @param arg2 Service-defined payload argument.
1069 * @param arg3 Service-defined payload argument.
1070 * @param arg4 Service-defined payload argument.
1071 * @param arg5 Service-defined payload argument.
1072 * @param r1 If non-NULL, storage for the 1st reply argument.
1073 * @param r2 If non-NULL, storage for the 2nd reply argument.
1074 * @param r3 If non-NULL, storage for the 3rd reply argument.
1075 * @param r4 If non-NULL, storage for the 4th reply argument.
1076 * @param r5 If non-NULL, storage for the 5th reply argument.
1077 *
1078 * @return Return code of the reply or a negative error code.
1079 *
1080 */
1081sysarg_t async_req_slow(int phoneid, sysarg_t method, sysarg_t arg1,
1082 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
1083 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
1084{
1085 ipc_call_t result;
1086 aid_t eid = async_send_5(phoneid, method, arg1, arg2, arg3, arg4, arg5,
1087 &result);
1088
1089 sysarg_t rc;
1090 async_wait_for(eid, &rc);
1091
1092 if (r1)
1093 *r1 = IPC_GET_ARG1(result);
1094
1095 if (r2)
1096 *r2 = IPC_GET_ARG2(result);
1097
1098 if (r3)
1099 *r3 = IPC_GET_ARG3(result);
1100
1101 if (r4)
1102 *r4 = IPC_GET_ARG4(result);
1103
1104 if (r5)
1105 *r5 = IPC_GET_ARG5(result);
1106
1107 return rc;
1108}
1109
1110/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
1111 *
1112 * Ask through phone for a new connection to some service.
1113 *
1114 * @param phoneid Phone handle used for contacting the other side.
1115 * @param arg1 User defined argument.
1116 * @param arg2 User defined argument.
1117 * @param arg3 User defined argument.
1118 *
1119 * @return New phone handle on success or a negative error code.
1120 */
1121int
1122async_connect_me_to(int phoneid, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
1123{
1124 int rc;
1125 sysarg_t newphid;
1126
1127 rc = async_req_3_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, NULL,
1128 NULL, NULL, NULL, &newphid);
1129
1130 if (rc != EOK)
1131 return rc;
1132
1133 return newphid;
1134}
1135
1136/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
1137 *
1138 * Ask through phone for a new connection to some service and block until
1139 * success.
1140 *
1141 * @param phoneid Phone handle used for contacting the other side.
1142 * @param arg1 User defined argument.
1143 * @param arg2 User defined argument.
1144 * @param arg3 User defined argument.
1145 *
1146 * @return New phone handle on success or a negative error code.
1147 */
1148int
1149async_connect_me_to_blocking(int phoneid, sysarg_t arg1, sysarg_t arg2,
1150 sysarg_t arg3)
1151{
1152 int rc;
1153 sysarg_t newphid;
1154
1155 rc = async_req_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3,
1156 IPC_FLAG_BLOCKING, NULL, NULL, NULL, NULL, &newphid);
1157
1158 if (rc != EOK)
1159 return rc;
1160
1161 return newphid;
1162}
1163
1164/** Wrapper for making IPC_M_SHARE_IN calls using the async framework.
1165 *
1166 * @param phoneid Phone that will be used to contact the receiving side.
1167 * @param dst Destination address space area base.
1168 * @param size Size of the destination address space area.
1169 * @param arg User defined argument.
1170 * @param flags Storage where the received flags will be stored. Can be
1171 * NULL.
1172 *
1173 * @return Zero on success or a negative error code from errno.h.
1174 */
1175int async_share_in_start(int phoneid, void *dst, size_t size, sysarg_t arg,
1176 int *flags)
1177{
1178 int res;
1179 sysarg_t tmp_flags;
1180 res = async_req_3_2(phoneid, IPC_M_SHARE_IN, (sysarg_t) dst,
1181 (sysarg_t) size, arg, NULL, &tmp_flags);
1182 if (flags)
1183 *flags = tmp_flags;
1184 return res;
1185}
1186
1187/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
1188 *
1189 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN calls
1190 * so that the user doesn't have to remember the meaning of each IPC argument.
1191 *
1192 * So far, this wrapper is to be used from within a connection fibril.
1193 *
1194 * @param callid Storage where the hash of the IPC_M_SHARE_IN call will
1195 * be stored.
1196 * @param size Destination address space area size.
1197 *
1198 * @return Non-zero on success, zero on failure.
1199 */
1200int async_share_in_receive(ipc_callid_t *callid, size_t *size)
1201{
1202 ipc_call_t data;
1203
1204 assert(callid);
1205 assert(size);
1206
1207 *callid = async_get_call(&data);
1208 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
1209 return 0;
1210 *size = (size_t) IPC_GET_ARG2(data);
1211 return 1;
1212}
1213
1214/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1215 *
1216 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls
1217 * so that the user doesn't have to remember the meaning of each IPC argument.
1218 *
1219 * @param callid Hash of the IPC_M_DATA_READ call to answer.
1220 * @param src Source address space base.
1221 * @param flags Flags to be used for sharing. Bits can be only cleared.
1222 *
1223 * @return Zero on success or a value from @ref errno.h on failure.
1224 */
1225int async_share_in_finalize(ipc_callid_t callid, void *src, int flags)
1226{
1227 return ipc_share_in_finalize(callid, src, flags);
1228}
1229
1230/** Wrapper for making IPC_M_SHARE_OUT calls using the async framework.
1231 *
1232 * @param phoneid Phone that will be used to contact the receiving side.
1233 * @param src Source address space area base address.
1234 * @param flags Flags to be used for sharing. Bits can be only cleared.
1235 *
1236 * @return Zero on success or a negative error code from errno.h.
1237 */
1238int async_share_out_start(int phoneid, void *src, int flags)
1239{
1240 return async_req_3_0(phoneid, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
1241 (sysarg_t) flags);
1242}
1243
1244/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1245 *
1246 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT calls
1247 * so that the user doesn't have to remember the meaning of each IPC argument.
1248 *
1249 * So far, this wrapper is to be used from within a connection fibril.
1250 *
1251 * @param callid Storage where the hash of the IPC_M_SHARE_OUT call will
1252 * be stored.
1253 * @param size Storage where the source address space area size will be
1254 * stored.
1255 * @param flags Storage where the sharing flags will be stored.
1256 *
1257 * @return Non-zero on success, zero on failure.
1258 */
1259int async_share_out_receive(ipc_callid_t *callid, size_t *size, int *flags)
1260{
1261 ipc_call_t data;
1262
1263 assert(callid);
1264 assert(size);
1265 assert(flags);
1266
1267 *callid = async_get_call(&data);
1268 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
1269 return 0;
1270 *size = (size_t) IPC_GET_ARG2(data);
1271 *flags = (int) IPC_GET_ARG3(data);
1272 return 1;
1273}
1274
1275/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1276 *
1277 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT calls
1278 * so that the user doesn't have to remember the meaning of each IPC argument.
1279 *
1280 * @param callid Hash of the IPC_M_DATA_WRITE call to answer.
1281 * @param dst Destination address space area base address.
1282 *
1283 * @return Zero on success or a value from @ref errno.h on failure.
1284 */
1285int async_share_out_finalize(ipc_callid_t callid, void *dst)
1286{
1287 return ipc_share_out_finalize(callid, dst);
1288}
1289
1290
1291/** Wrapper for making IPC_M_DATA_READ calls using the async framework.
1292 *
1293 * @param phoneid Phone that will be used to contact the receiving side.
1294 * @param dst Address of the beginning of the destination buffer.
1295 * @param size Size of the destination buffer.
1296 *
1297 * @return Zero on success or a negative error code from errno.h.
1298 */
1299int async_data_read_start(int phoneid, void *dst, size_t size)
1300{
1301 return async_req_2_0(phoneid, IPC_M_DATA_READ, (sysarg_t) dst,
1302 (sysarg_t) size);
1303}
1304
1305/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1306 *
1307 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ calls
1308 * so that the user doesn't have to remember the meaning of each IPC argument.
1309 *
1310 * So far, this wrapper is to be used from within a connection fibril.
1311 *
1312 * @param callid Storage where the hash of the IPC_M_DATA_READ call will
1313 * be stored.
1314 * @param size Storage where the maximum size will be stored. Can be
1315 * NULL.
1316 *
1317 * @return Non-zero on success, zero on failure.
1318 */
1319int async_data_read_receive(ipc_callid_t *callid, size_t *size)
1320{
1321 ipc_call_t data;
1322
1323 assert(callid);
1324
1325 *callid = async_get_call(&data);
1326 if (IPC_GET_IMETHOD(data) != IPC_M_DATA_READ)
1327 return 0;
1328 if (size)
1329 *size = (size_t) IPC_GET_ARG2(data);
1330 return 1;
1331}
1332
1333/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
1334 *
1335 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls
1336 * so that the user doesn't have to remember the meaning of each IPC argument.
1337 *
1338 * @param callid Hash of the IPC_M_DATA_READ call to answer.
1339 * @param src Source address for the IPC_M_DATA_READ call.
1340 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
1341 * the maximum size announced by the sender.
1342 *
1343 * @return Zero on success or a value from @ref errno.h on failure.
1344 */
1345int async_data_read_finalize(ipc_callid_t callid, const void *src, size_t size)
1346{
1347 return ipc_data_read_finalize(callid, src, size);
1348}
1349
1350/** Wrapper for forwarding any read request
1351 *
1352 *
1353 */
1354int async_data_read_forward_fast(int phoneid, sysarg_t method, sysarg_t arg1,
1355 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
1356{
1357 ipc_callid_t callid;
1358 if (!async_data_read_receive(&callid, NULL)) {
1359 ipc_answer_0(callid, EINVAL);
1360 return EINVAL;
1361 }
1362
1363 aid_t msg = async_send_fast(phoneid, method, arg1, arg2, arg3, arg4,
1364 dataptr);
1365 if (msg == 0) {
1366 ipc_answer_0(callid, EINVAL);
1367 return EINVAL;
1368 }
1369
1370 int retval = ipc_forward_fast(callid, phoneid, 0, 0, 0,
1371 IPC_FF_ROUTE_FROM_ME);
1372 if (retval != EOK) {
1373 async_wait_for(msg, NULL);
1374 ipc_answer_0(callid, retval);
1375 return retval;
1376 }
1377
1378 sysarg_t rc;
1379 async_wait_for(msg, &rc);
1380
1381 return (int) rc;
1382}
1383
1384/** Wrapper for making IPC_M_DATA_WRITE calls using the async framework.
1385 *
1386 * @param phoneid Phone that will be used to contact the receiving side.
1387 * @param src Address of the beginning of the source buffer.
1388 * @param size Size of the source buffer.
1389 *
1390 * @return Zero on success or a negative error code from errno.h.
1391 *
1392 */
1393int async_data_write_start(int phoneid, const void *src, size_t size)
1394{
1395 return async_req_2_0(phoneid, IPC_M_DATA_WRITE, (sysarg_t) src,
1396 (sysarg_t) size);
1397}
1398
1399/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1400 *
1401 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE calls
1402 * so that the user doesn't have to remember the meaning of each IPC argument.
1403 *
1404 * So far, this wrapper is to be used from within a connection fibril.
1405 *
1406 * @param callid Storage where the hash of the IPC_M_DATA_WRITE call will
1407 * be stored.
1408 * @param size Storage where the suggested size will be stored. May be
1409 * NULL
1410 *
1411 * @return Non-zero on success, zero on failure.
1412 *
1413 */
1414int async_data_write_receive(ipc_callid_t *callid, size_t *size)
1415{
1416 ipc_call_t data;
1417
1418 assert(callid);
1419
1420 *callid = async_get_call(&data);
1421 if (IPC_GET_IMETHOD(data) != IPC_M_DATA_WRITE)
1422 return 0;
1423
1424 if (size)
1425 *size = (size_t) IPC_GET_ARG2(data);
1426
1427 return 1;
1428}
1429
1430/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
1431 *
1432 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE calls
1433 * so that the user doesn't have to remember the meaning of each IPC argument.
1434 *
1435 * @param callid Hash of the IPC_M_DATA_WRITE call to answer.
1436 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
1437 * @param size Final size for the IPC_M_DATA_WRITE call.
1438 *
1439 * @return Zero on success or a value from @ref errno.h on failure.
1440 *
1441 */
1442int async_data_write_finalize(ipc_callid_t callid, void *dst, size_t size)
1443{
1444 return ipc_data_write_finalize(callid, dst, size);
1445}
1446
1447/** Wrapper for receiving binary data or strings
1448 *
1449 * This wrapper only makes it more comfortable to use async_data_write_*
1450 * functions to receive binary data or strings.
1451 *
1452 * @param data Pointer to data pointer (which should be later disposed
1453 * by free()). If the operation fails, the pointer is not
1454 * touched.
1455 * @param nullterm If true then the received data is always zero terminated.
1456 * This also causes to allocate one extra byte beyond the
1457 * raw transmitted data.
1458 * @param min_size Minimum size (in bytes) of the data to receive.
1459 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
1460 * no limit.
1461 * @param granulariy If non-zero then the size of the received data has to
1462 * be divisible by this value.
1463 * @param received If not NULL, the size of the received data is stored here.
1464 *
1465 * @return Zero on success or a value from @ref errno.h on failure.
1466 *
1467 */
1468int async_data_write_accept(void **data, const bool nullterm,
1469 const size_t min_size, const size_t max_size, const size_t granularity,
1470 size_t *received)
1471{
1472 ipc_callid_t callid;
1473 size_t size;
1474 if (!async_data_write_receive(&callid, &size)) {
1475 ipc_answer_0(callid, EINVAL);
1476 return EINVAL;
1477 }
1478
1479 if (size < min_size) {
1480 ipc_answer_0(callid, EINVAL);
1481 return EINVAL;
1482 }
1483
1484 if ((max_size > 0) && (size > max_size)) {
1485 ipc_answer_0(callid, EINVAL);
1486 return EINVAL;
1487 }
1488
1489 if ((granularity > 0) && ((size % granularity) != 0)) {
1490 ipc_answer_0(callid, EINVAL);
1491 return EINVAL;
1492 }
1493
1494 void *_data;
1495
1496 if (nullterm)
1497 _data = malloc(size + 1);
1498 else
1499 _data = malloc(size);
1500
1501 if (_data == NULL) {
1502 ipc_answer_0(callid, ENOMEM);
1503 return ENOMEM;
1504 }
1505
1506 int rc = async_data_write_finalize(callid, _data, size);
1507 if (rc != EOK) {
1508 free(_data);
1509 return rc;
1510 }
1511
1512 if (nullterm)
1513 ((char *) _data)[size] = 0;
1514
1515 *data = _data;
1516 if (received != NULL)
1517 *received = size;
1518
1519 return EOK;
1520}
1521
1522/** Wrapper for voiding any data that is about to be received
1523 *
1524 * This wrapper can be used to void any pending data
1525 *
1526 * @param retval Error value from @ref errno.h to be returned to the caller.
1527 *
1528 */
1529void async_data_write_void(const int retval)
1530{
1531 ipc_callid_t callid;
1532 async_data_write_receive(&callid, NULL);
1533 ipc_answer_0(callid, retval);
1534}
1535
1536/** Wrapper for forwarding any data that is about to be received
1537 *
1538 *
1539 */
1540int async_data_write_forward_fast(int phoneid, sysarg_t method, sysarg_t arg1,
1541 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
1542{
1543 ipc_callid_t callid;
1544 if (!async_data_write_receive(&callid, NULL)) {
1545 ipc_answer_0(callid, EINVAL);
1546 return EINVAL;
1547 }
1548
1549 aid_t msg = async_send_fast(phoneid, method, arg1, arg2, arg3, arg4,
1550 dataptr);
1551 if (msg == 0) {
1552 ipc_answer_0(callid, EINVAL);
1553 return EINVAL;
1554 }
1555
1556 int retval = ipc_forward_fast(callid, phoneid, 0, 0, 0,
1557 IPC_FF_ROUTE_FROM_ME);
1558 if (retval != EOK) {
1559 async_wait_for(msg, NULL);
1560 ipc_answer_0(callid, retval);
1561 return retval;
1562 }
1563
1564 sysarg_t rc;
1565 async_wait_for(msg, &rc);
1566
1567 return (int) rc;
1568}
1569
1570/** @}
1571 */
Note: See TracBrowser for help on using the repository browser.