source: mainline/uspace/lib/libc/generic/ipc.c@ 5d4e90f0

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

VFS work.
Implement VFS_REGISTER part of the protocol in the FAT file system.

IPC work.
Rename ipc_data_send_accept() to ipc_data_receive() and ipc_data_send_answer()
to ipc_data_deliver(). Introduce ipc_data_send().

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