source: mainline/uspace/lib/libc/generic/ipc.c@ 00acd66

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

New, better-structured, directory layout for uspace.

  • Property mode set to 100644
File size: 16.8 KB
RevLine 
[b419162]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
[b419162]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.
[b2951e2]27 */
28
29/** @addtogroup libc
30 * @{
31 * @}
32 */
33
[fadd381]34/** @addtogroup libcipc IPC
[b2951e2]35 * @brief HelenOS uspace IPC
36 * @{
37 * @ingroup libc
38 */
39/** @file
[b419162]40 */
41
[7ee6aff]42#include <ipc/ipc.h>
[b419162]43#include <libc.h>
[936351c1]44#include <malloc.h>
45#include <errno.h>
[7ee6aff]46#include <libadt/list.h>
[936351c1]47#include <stdio.h>
48#include <unistd.h>
[35509652]49#include <futex.h>
[04a73cdf]50#include <kernel/synch/synch.h>
[fc42b28]51#include <async.h>
52#include <psthread.h>
[b419162]53
[8b243f2]54/** Structure used for keeping track of sent asynchronous calls and queing
55 * unsent calls.
[936351c1]56 */
57typedef struct {
58 link_t list;
59
60 ipc_async_callback_t callback;
61 void *private;
62 union {
63 ipc_callid_t callid;
64 struct {
[4c61e60]65 ipc_call_t data;
[936351c1]66 int phoneid;
67 } msg;
[8b243f2]68 } u;
69 pstid_t ptid; /**< Pseudothread waiting for sending this call. */
[936351c1]70} async_call_t;
71
72LIST_INITIALIZE(dispatched_calls);
[fc42b28]73
[8b243f2]74/** List of asynchronous calls that were not accepted by kernel.
75 *
76 * It is protected by async_futex, because if the call cannot be sent into the
77 * kernel, the async framework is used automatically.
[fc42b28]78 */
[8b243f2]79LIST_INITIALIZE(queued_calls);
[936351c1]80
[80649a91]81static atomic_t ipc_futex = FUTEX_INITIALIZER;
[35509652]82
[8b243f2]83/** Make a fast synchronous call.
84 *
85 * Only one payload argument can be passed using this function. However, this
86 * function is faster than the generic ipc_call_sync_3().
87 *
88 * @param phoneid Phone handle for the call.
89 * @param method Requested method.
90 * @param arg1 Service-defined payload argument.
91 * @param result If non-NULL, the return ARG1 will be stored there.
92 *
93 * @return Negative values represent errors returned by IPC.
94 * Otherwise the RETVAL of the answer is returned.
95 */
96int ipc_call_sync(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t *result)
[b419162]97{
[4c61e60]98 ipc_call_t resdata;
[06502f7d]99 int callres;
100
[936351c1]101 callres = __SYSCALL4(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1,
[8b243f2]102 (sysarg_t) &resdata);
[06502f7d]103 if (callres)
104 return callres;
105 if (result)
106 *result = IPC_GET_ARG1(resdata);
107 return IPC_GET_RETVAL(resdata);
[b419162]108}
109
[8b243f2]110/** Make a synchronous call transmitting 3 arguments of payload.
111 *
112 * @param phoneid Phone handle for the call.
113 * @param method Requested method.
114 * @param arg1 Service-defined payload argument.
115 * @param arg2 Service-defined payload argument.
116 * @param arg3 Service-defined payload argument.
117 * @param result1 If non-NULL, storage for the first return argument.
118 * @param result2 If non-NULL, storage for the second return argument.
119 * @param result3 If non-NULL, storage for the third return argument.
120 *
121 * @return Negative value means IPC error.
122 * Otherwise the RETVAL of the answer.
123 */
124int ipc_call_sync_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
125 ipcarg_t arg3, ipcarg_t *result1, ipcarg_t *result2, ipcarg_t *result3)
[06502f7d]126{
[4c61e60]127 ipc_call_t data;
[06502f7d]128 int callres;
129
130 IPC_SET_METHOD(data, method);
131 IPC_SET_ARG1(data, arg1);
132 IPC_SET_ARG2(data, arg2);
133 IPC_SET_ARG3(data, arg3);
134
[8b243f2]135 callres = __SYSCALL3(SYS_IPC_CALL_SYNC, phoneid, (sysarg_t) &data,
136 (sysarg_t) &data);
[06502f7d]137 if (callres)
138 return callres;
139
140 if (result1)
141 *result1 = IPC_GET_ARG1(data);
142 if (result2)
143 *result2 = IPC_GET_ARG2(data);
144 if (result3)
145 *result3 = IPC_GET_ARG3(data);
146 return IPC_GET_RETVAL(data);
147}
148
[8b243f2]149/** Syscall to send asynchronous message.
150 *
151 * @param phoneid Phone handle for the call.
152 * @param data Call data with the request.
153 *
154 * @return Hash of the call or an error code.
155 */
156static ipc_callid_t _ipc_call_async(int phoneid, ipc_call_t *data)
[936351c1]157{
[8b243f2]158 return __SYSCALL2(SYS_IPC_CALL_ASYNC, phoneid, (sysarg_t) data);
[936351c1]159}
160
[8b243f2]161/** Prolog to ipc_call_async_*() functions.
162 *
163 * @param private Argument for the answer/error callback.
164 * @param callback Answer/error callback.
165 *
166 * @return New, partially initialized async_call structure or NULL.
167 */
168static inline async_call_t *ipc_prepare_async(void *private,
169 ipc_async_callback_t callback)
[b419162]170{
[936351c1]171 async_call_t *call;
[06502f7d]172
[936351c1]173 call = malloc(sizeof(*call));
174 if (!call) {
[a784a96]175 if (callback)
176 callback(private, ENOMEM, NULL);
[c1d2c9d]177 return NULL;
[936351c1]178 }
[fc42b28]179 call->callback = callback;
180 call->private = private;
181
[c1d2c9d]182 return call;
183}
184
[8b243f2]185/** Epilogue of ipc_call_async_*() functions.
186 *
187 * @param callid Value returned by the SYS_IPC_CALL_ASYNC_* syscall.
188 * @param phoneid Phone handle through which the call was made.
189 * @param call async_call structure returned by ipc_prepare_async().
190 * @param can_preempt If non-zero, the current pseudo thread can be preempted
191 * in this call.
192 */
193static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
194 async_call_t *call, int can_preempt)
[c1d2c9d]195{
[d8b42fb2]196 if (!call) { /* Nothing to do regardless if failed or not */
197 futex_up(&ipc_futex);
198 return;
199 }
200
[06502f7d]201 if (callid == IPC_CALLRET_FATAL) {
[fc42b28]202 futex_up(&ipc_futex);
[06502f7d]203 /* Call asynchronous handler with error code */
[c1d2c9d]204 if (call->callback)
205 call->callback(call->private, ENOENT, NULL);
[936351c1]206 free(call);
[06502f7d]207 return;
208 }
[936351c1]209
[06502f7d]210 if (callid == IPC_CALLRET_TEMPORARY) {
[fc42b28]211 futex_up(&ipc_futex);
212
[936351c1]213 call->u.msg.phoneid = phoneid;
[b1f51f0]214
[fc42b28]215 futex_down(&async_futex);
[936351c1]216 list_append(&call->list, &queued_calls);
[fc42b28]217
[b1f51f0]218 if (can_preempt) {
219 call->ptid = psthread_get_id();
220 psthread_schedule_next_adv(PS_TO_MANAGER);
221 /* Async futex unlocked by previous call */
222 } else {
223 call->ptid = 0;
224 futex_up(&async_futex);
225 }
[06502f7d]226 return;
227 }
[936351c1]228 call->u.callid = callid;
[8b243f2]229 /* Add call to the list of dispatched calls */
[936351c1]230 list_append(&call->list, &dispatched_calls);
[35509652]231 futex_up(&ipc_futex);
[c1d2c9d]232
233}
234
[8b243f2]235/** Make a fast asynchronous call.
236 *
237 * This function can only handle two arguments of payload. It is, however,
238 * faster than the more generic ipc_call_async_3().
239 *
240 * Note that this function is a void function.
241 * During normal opertation, answering this call will trigger the callback.
242 * In case of fatal error, call the callback handler with the proper error code.
243 * If the call cannot be temporarily made, queue it.
[c1d2c9d]244 *
[8b243f2]245 * @param phoneid Phone handle for the call.
246 * @param method Requested method.
247 * @param arg1 Service-defined payload argument.
248 * @param arg2 Service-defined payload argument.
249 * @param private Argument to be passed to the answer/error callback.
250 * @param callback Answer or error callback.
251 * @param can_preempt If non-zero, the current pseudo thread will be preempted
252 * in case the kernel temporarily refuses to accept more
253 * asynchronous calls.
[c1d2c9d]254 */
255void ipc_call_async_2(int phoneid, ipcarg_t method, ipcarg_t arg1,
[8b243f2]256 ipcarg_t arg2, void *private, ipc_async_callback_t callback,
257 int can_preempt)
[c1d2c9d]258{
[d8b42fb2]259 async_call_t *call = NULL;
[c1d2c9d]260 ipc_callid_t callid;
261
[d8b42fb2]262 if (callback) {
263 call = ipc_prepare_async(private, callback);
264 if (!call)
265 return;
266 }
[c1d2c9d]267
[8b243f2]268 /*
269 * We need to make sure that we get callid before another thread
270 * accesses the queue again.
271 */
[c1d2c9d]272 futex_down(&ipc_futex);
[8b243f2]273 callid = __SYSCALL4(SYS_IPC_CALL_ASYNC_FAST, phoneid, method, arg1,
274 arg2);
[c1d2c9d]275
276 if (callid == IPC_CALLRET_TEMPORARY) {
[d8b42fb2]277 if (!call) {
278 call = ipc_prepare_async(private, callback);
279 if (!call)
280 return;
281 }
[c1d2c9d]282 IPC_SET_METHOD(call->u.msg.data, method);
283 IPC_SET_ARG1(call->u.msg.data, arg1);
284 IPC_SET_ARG2(call->u.msg.data, arg2);
285 }
[b1f51f0]286 ipc_finish_async(callid, phoneid, call, can_preempt);
[c1d2c9d]287}
288
[8b243f2]289/** Make an asynchronous call transmitting the entire payload.
290 *
291 * Note that this function is a void function.
292 * During normal opertation, answering this call will trigger the callback.
293 * In case of fatal error, call the callback handler with the proper error code.
294 * If the call cannot be temporarily made, queue it.
295 *
296 * @param phoneid Phone handle for the call.
297 * @param method Requested method.
298 * @param arg1 Service-defined payload argument.
299 * @param arg2 Service-defined payload argument.
300 * @param arg3 Service-defined payload argument.
301 * @param private Argument to be passed to the answer/error callback.
302 * @param callback Answer or error callback.
303 * @param can_preempt If non-zero, the current pseudo thread will be preempted
304 * in case the kernel temporarily refuses to accept more
305 * asynchronous calls.
[c1d2c9d]306 *
307 */
308void ipc_call_async_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
[8b243f2]309 ipcarg_t arg2, ipcarg_t arg3, void *private, ipc_async_callback_t callback,
310 int can_preempt)
[c1d2c9d]311{
312 async_call_t *call;
313 ipc_callid_t callid;
314
315 call = ipc_prepare_async(private, callback);
316 if (!call)
317 return;
318
319 IPC_SET_METHOD(call->u.msg.data, method);
320 IPC_SET_ARG1(call->u.msg.data, arg1);
321 IPC_SET_ARG2(call->u.msg.data, arg2);
322 IPC_SET_ARG3(call->u.msg.data, arg3);
[8b243f2]323 /*
324 * We need to make sure that we get callid before another thread accesses
325 * the queue again.
326 */
[c1d2c9d]327 futex_down(&ipc_futex);
328 callid = _ipc_call_async(phoneid, &call->u.msg.data);
329
[b1f51f0]330 ipc_finish_async(callid, phoneid, call, can_preempt);
[b419162]331}
332
[06502f7d]333
[8b243f2]334/** Answer a received call - fast version.
[250717cc]335 *
[8b243f2]336 * The fast answer makes use of passing retval and first two arguments in
337 * registers. If you need to return more, use the ipc_answer() instead.
[250717cc]338 *
[8b243f2]339 * @param callid Hash of the call being answered.
340 * @param retval Return value.
341 * @param arg1 First return argument.
342 * @param arg2 Second return argument.
[250717cc]343 *
[8b243f2]344 * @return Zero on success or a value from @ref errno.h on failure.
[250717cc]345 */
346ipcarg_t ipc_answer_fast(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1,
[8b243f2]347 ipcarg_t arg2)
[b419162]348{
[8a568e3]349 return __SYSCALL4(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2);
[b419162]350}
[06502f7d]351
[8b243f2]352/** Answer a received call - full version.
[250717cc]353 *
[8b243f2]354 * @param callid Hash of the call being answered.
355 * @param call Call structure with the answer.
356 * Must be already initialized by the responder.
[250717cc]357 *
[8b243f2]358 * @return Zero on success or a value from @ref errno.h on failure.
[250717cc]359 */
360ipcarg_t ipc_answer(ipc_callid_t callid, ipc_call_t *call)
361{
362 return __SYSCALL2(SYS_IPC_ANSWER, callid, (sysarg_t) call);
363}
364
365
[8b243f2]366/** Try to dispatch queued calls from the async queue. */
[936351c1]367static void try_dispatch_queued_calls(void)
368{
369 async_call_t *call;
370 ipc_callid_t callid;
371
[8b243f2]372 /** @todo
373 * Integrate intelligently ipc_futex, so that it is locked during
374 * ipc_call_async_*(), until it is added to dispatched_calls.
[fc42b28]375 */
376 futex_down(&async_futex);
[936351c1]377 while (!list_empty(&queued_calls)) {
[8b243f2]378 call = list_get_instance(queued_calls.next, async_call_t, list);
379 callid = _ipc_call_async(call->u.msg.phoneid, &call->u.msg.data);
[fc42b28]380 if (callid == IPC_CALLRET_TEMPORARY) {
[936351c1]381 break;
[fc42b28]382 }
[936351c1]383 list_remove(&call->list);
[35509652]384
[fc42b28]385 futex_up(&async_futex);
[b1f51f0]386 if (call->ptid)
387 psthread_add_ready(call->ptid);
[fc42b28]388
[936351c1]389 if (callid == IPC_CALLRET_FATAL) {
[a784a96]390 if (call->callback)
391 call->callback(call->private, ENOENT, NULL);
[936351c1]392 free(call);
393 } else {
394 call->u.callid = callid;
[fc42b28]395 futex_down(&ipc_futex);
[936351c1]396 list_append(&call->list, &dispatched_calls);
[fc42b28]397 futex_up(&ipc_futex);
[936351c1]398 }
[fc42b28]399 futex_down(&async_futex);
[936351c1]400 }
[fc42b28]401 futex_up(&async_futex);
[936351c1]402}
403
[8b243f2]404/** Handle a received answer.
[936351c1]405 *
[8b243f2]406 * Find the hash of the answer and call the answer callback.
[936351c1]407 *
[8b243f2]408 * @todo Make it use hash table.
409 *
410 * @param callid Hash of the received answer.
411 * The answer has the same hash as the request OR'ed with
412 * the IPC_CALLID_ANSWERED bit.
413 * @param data Call data of the answer.
[936351c1]414 */
[4c61e60]415static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
[936351c1]416{
417 link_t *item;
418 async_call_t *call;
419
420 callid &= ~IPC_CALLID_ANSWERED;
421
[35509652]422 futex_down(&ipc_futex);
[936351c1]423 for (item = dispatched_calls.next; item != &dispatched_calls;
[8b243f2]424 item = item->next) {
[936351c1]425 call = list_get_instance(item, async_call_t, list);
426 if (call->u.callid == callid) {
427 list_remove(&call->list);
[35509652]428 futex_up(&ipc_futex);
[a784a96]429 if (call->callback)
430 call->callback(call->private,
[8b243f2]431 IPC_GET_RETVAL(*data), data);
[a784a96]432 free(call);
[936351c1]433 return;
434 }
435 }
[35509652]436 futex_up(&ipc_futex);
[936351c1]437}
438
439
[8b243f2]440/** Wait for a first call to come.
441 *
442 * @param call Storage where the incoming call data will be stored.
443 * @param usec Timeout in microseconds
444 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
[b419162]445 *
[8b243f2]446 * @return Hash of the call. Note that certain bits have special
447 * meaning. IPC_CALLID_ANSWERED will be set in an answer
448 * and IPC_CALLID_NOTIFICATION is used for notifications.
449 *
[b419162]450 */
[80649a91]451ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec, int flags)
[b419162]452{
453 ipc_callid_t callid;
454
[80649a91]455 callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
456 /* Handle received answers */
[fc42b28]457 if (callid & IPC_CALLID_ANSWERED) {
[80649a91]458 handle_answer(callid, call);
[fc42b28]459 try_dispatch_queued_calls();
460 }
[04a73cdf]461
462 return callid;
463}
464
465/** Wait some time for an IPC call.
466 *
[8b243f2]467 * The call will return after an answer is received.
468 *
469 * @param call Storage where the incoming call data will be stored.
470 * @param usec Timeout in microseconds.
[096ba7a]471 *
[8b243f2]472 * @return Hash of the answer.
[04a73cdf]473 */
474ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, uint32_t usec)
475{
476 ipc_callid_t callid;
477
478 do {
[2d22049]479 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
[04a73cdf]480 } while (callid & IPC_CALLID_ANSWERED);
481
482 return callid;
483}
484
485/** Check if there is an IPC call waiting to be picked up.
486 *
[8b243f2]487 * @param call Storage where the incoming call will be stored.
488 * @return Hash of the answer.
[04a73cdf]489 */
490ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
491{
492 ipc_callid_t callid;
493
494 do {
[8b243f2]495 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
496 SYNCH_FLAGS_NON_BLOCKING);
[06502f7d]497 } while (callid & IPC_CALLID_ANSWERED);
[936351c1]498
[b419162]499 return callid;
500}
[5106e98]501
[51ec40f]502/** Ask destination to do a callback connection.
[4c61e60]503 *
[8b243f2]504 * @param phoneid Phone handle used for contacting the other side.
505 * @param arg1 Service-defined argument.
506 * @param arg2 Service-defined argument.
507 * @param phonehash Storage where the library will store an opaque
[51ec40f]508 * identifier of the phone that will be used for incoming
[8b243f2]509 * calls. This identifier can be used for connection
510 * tracking.
511 *
512 * @return Zero on success or a negative error code.
[4c61e60]513 */
[51ec40f]514int ipc_connect_to_me(int phoneid, int arg1, int arg2, ipcarg_t *phonehash)
[5106e98]515{
[51ec40f]516 return ipc_call_sync_3(phoneid, IPC_M_CONNECT_TO_ME, arg1, arg2, 0, 0, 0,
517 phonehash);
[5106e98]518}
[11eae82]519
[51ec40f]520/** Ask through phone for a new connection to some service.
[4c61e60]521 *
[8b243f2]522 * @param phoneid Phone handle used for contacting the other side.
[51ec40f]523 * @param arg1 User defined argument.
524 * @param arg2 User defined argument.
525 *
[8b243f2]526 * @return New phone handle on success or a negative error code.
[4c61e60]527 */
[11eae82]528int ipc_connect_me_to(int phoneid, int arg1, int arg2)
529{
[06b0d112]530 ipcarg_t newphid;
[4c61e60]531 int res;
532
[51ec40f]533 res = ipc_call_sync_3(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, 0, 0, 0,
534 &newphid);
[4c61e60]535 if (res)
536 return res;
537 return newphid;
[11eae82]538}
539
[8b243f2]540/** Hang up a phone.
541 *
542 * @param phoneid Handle of the phone to be hung up.
543 *
544 * @return Zero on success or a negative error code.
545 */
[7048773]546int ipc_hangup(int phoneid)
547{
548 return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
549}
[6180b57]550
[2b017ba]551/** Register IRQ notification.
552 *
[8b243f2]553 * @param inr IRQ number.
554 * @param devno Device number of the device generating inr.
555 * @param method Use this method for notifying me.
556 * @param ucode Top-half pseudocode handler.
[2b017ba]557 *
[8b243f2]558 * @return Value returned by the kernel.
[2b017ba]559 */
560int ipc_register_irq(int inr, int devno, int method, irq_code_t *ucode)
[6180b57]561{
[8b243f2]562 return __SYSCALL4(SYS_IPC_REGISTER_IRQ, inr, devno, method,
563 (sysarg_t) ucode);
[6180b57]564}
565
[2b017ba]566/** Unregister IRQ notification.
567 *
[8b243f2]568 * @param inr IRQ number.
569 * @param devno Device number of the device generating inr.
[2b017ba]570 *
[8b243f2]571 * @return Value returned by the kernel.
[2b017ba]572 */
573int ipc_unregister_irq(int inr, int devno)
[6180b57]574{
[2b017ba]575 return __SYSCALL2(SYS_IPC_UNREGISTER_IRQ, inr, devno);
[6180b57]576}
[8a568e3]577
[8b243f2]578/** Forward a received call to another destination.
579 *
580 * @param callid Hash of the call to forward.
581 * @param phoneid Phone handle to use for forwarding.
582 * @param method New method for the forwarded call.
583 * @param arg1 New value of the first argument for the forwarded call.
584 *
585 * @return Zero on success or an error code.
586 *
587 * For non-system methods, the old method and arg1 are rewritten by the new
588 * values. For system methods, the new method and arg1 are written to the old
589 * arg1 and arg2, respectivelly.
590 */
[043dcc27]591int ipc_forward_fast(ipc_callid_t callid, int phoneid, int method, ipcarg_t arg1)
592{
593 return __SYSCALL4(SYS_IPC_FORWARD_FAST, callid, phoneid, method, arg1);
594}
595
[fadd381]596/** @}
[b2951e2]597 */
Note: See TracBrowser for help on using the repository browser.