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

Last change on this file since 103939e was 5e801dc, checked in by GitHub <noreply@…>, 6 years ago

Indicate and enforce constness of hash table key in certain functions (#158)

The assumption here is that modifying key in the hash/equal functions in something completely unexpected, and not something you would ever want to do intentionally, so it makes sense to disallow it entirely to get that extra level of checking.

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