source: mainline/uspace/lib/libc/generic/ipc.c@ 2e51969

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

Modify synchronous IPC to make use of all six syscall arguments. The preferred
means of synchronous communication is now via the set of ipc_call_sync_m_n()
macros, where m is the number of payload arguments passed to the kernel and n is
the number of return values. These macros will automatically decide between the
fast and the universal slow version of ipc_call_sync.

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