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

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

Improve comments in async.c

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