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
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 <psthread.h>
53
54/** Structure used for keeping track of sent asynchronous calls and queing
55 * unsent calls.
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 {
65 ipc_call_t data;
66 int phoneid;
67 } msg;
68 } u;
69 pstid_t ptid; /**< Pseudothread waiting for sending this call. */
70} async_call_t;
71
72LIST_INITIALIZE(dispatched_calls);
73
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.
78 */
79LIST_INITIALIZE(queued_calls);
80
81static atomic_t ipc_futex = FUTEX_INITIALIZER;
82
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)
97{
98 ipc_call_t resdata;
99 int callres;
100
101 callres = __SYSCALL4(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1,
102 (sysarg_t) &resdata);
103 if (callres)
104 return callres;
105 if (result)
106 *result = IPC_GET_ARG1(resdata);
107 return IPC_GET_RETVAL(resdata);
108}
109
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)
126{
127 ipc_call_t data;
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
135 callres = __SYSCALL3(SYS_IPC_CALL_SYNC, phoneid, (sysarg_t) &data,
136 (sysarg_t) &data);
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
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)
157{
158 return __SYSCALL2(SYS_IPC_CALL_ASYNC, phoneid, (sysarg_t) data);
159}
160
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)
170{
171 async_call_t *call;
172
173 call = malloc(sizeof(*call));
174 if (!call) {
175 if (callback)
176 callback(private, ENOMEM, NULL);
177 return NULL;
178 }
179 call->callback = callback;
180 call->private = private;
181
182 return call;
183}
184
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)
195{
196 if (!call) { /* Nothing to do regardless if failed or not */
197 futex_up(&ipc_futex);
198 return;
199 }
200
201 if (callid == IPC_CALLRET_FATAL) {
202 futex_up(&ipc_futex);
203 /* Call asynchronous handler with error code */
204 if (call->callback)
205 call->callback(call->private, ENOENT, NULL);
206 free(call);
207 return;
208 }
209
210 if (callid == IPC_CALLRET_TEMPORARY) {
211 futex_up(&ipc_futex);
212
213 call->u.msg.phoneid = phoneid;
214
215 futex_down(&async_futex);
216 list_append(&call->list, &queued_calls);
217
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 }
226 return;
227 }
228 call->u.callid = callid;
229 /* Add call to the list of dispatched calls */
230 list_append(&call->list, &dispatched_calls);
231 futex_up(&ipc_futex);
232
233}
234
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.
244 *
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.
254 */
255void ipc_call_async_2(int phoneid, ipcarg_t method, ipcarg_t arg1,
256 ipcarg_t arg2, void *private, ipc_async_callback_t callback,
257 int can_preempt)
258{
259 async_call_t *call = NULL;
260 ipc_callid_t callid;
261
262 if (callback) {
263 call = ipc_prepare_async(private, callback);
264 if (!call)
265 return;
266 }
267
268 /*
269 * We need to make sure that we get callid before another thread
270 * accesses the queue again.
271 */
272 futex_down(&ipc_futex);
273 callid = __SYSCALL4(SYS_IPC_CALL_ASYNC_FAST, phoneid, method, arg1,
274 arg2);
275
276 if (callid == IPC_CALLRET_TEMPORARY) {
277 if (!call) {
278 call = ipc_prepare_async(private, callback);
279 if (!call)
280 return;
281 }
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 }
286 ipc_finish_async(callid, phoneid, call, can_preempt);
287}
288
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.
306 *
307 */
308void ipc_call_async_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
309 ipcarg_t arg2, ipcarg_t arg3, void *private, ipc_async_callback_t callback,
310 int can_preempt)
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);
323 /*
324 * We need to make sure that we get callid before another thread accesses
325 * the queue again.
326 */
327 futex_down(&ipc_futex);
328 callid = _ipc_call_async(phoneid, &call->u.msg.data);
329
330 ipc_finish_async(callid, phoneid, call, can_preempt);
331}
332
333
334/** Answer a received call - fast version.
335 *
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.
338 *
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.
343 *
344 * @return Zero on success or a value from @ref errno.h on failure.
345 */
346ipcarg_t ipc_answer_fast(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1,
347 ipcarg_t arg2)
348{
349 return __SYSCALL4(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2);
350}
351
352/** Answer a received call - full version.
353 *
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.
357 *
358 * @return Zero on success or a value from @ref errno.h on failure.
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
366/** Try to dispatch queued calls from the async queue. */
367static void try_dispatch_queued_calls(void)
368{
369 async_call_t *call;
370 ipc_callid_t callid;
371
372 /** @todo
373 * Integrate intelligently ipc_futex, so that it is locked during
374 * ipc_call_async_*(), until it is added to dispatched_calls.
375 */
376 futex_down(&async_futex);
377 while (!list_empty(&queued_calls)) {
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);
380 if (callid == IPC_CALLRET_TEMPORARY) {
381 break;
382 }
383 list_remove(&call->list);
384
385 futex_up(&async_futex);
386 if (call->ptid)
387 psthread_add_ready(call->ptid);
388
389 if (callid == IPC_CALLRET_FATAL) {
390 if (call->callback)
391 call->callback(call->private, ENOENT, NULL);
392 free(call);
393 } else {
394 call->u.callid = callid;
395 futex_down(&ipc_futex);
396 list_append(&call->list, &dispatched_calls);
397 futex_up(&ipc_futex);
398 }
399 futex_down(&async_futex);
400 }
401 futex_up(&async_futex);
402}
403
404/** Handle a received answer.
405 *
406 * Find the hash of the answer and call the answer callback.
407 *
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.
414 */
415static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
416{
417 link_t *item;
418 async_call_t *call;
419
420 callid &= ~IPC_CALLID_ANSWERED;
421
422 futex_down(&ipc_futex);
423 for (item = dispatched_calls.next; item != &dispatched_calls;
424 item = item->next) {
425 call = list_get_instance(item, async_call_t, list);
426 if (call->u.callid == callid) {
427 list_remove(&call->list);
428 futex_up(&ipc_futex);
429 if (call->callback)
430 call->callback(call->private,
431 IPC_GET_RETVAL(*data), data);
432 free(call);
433 return;
434 }
435 }
436 futex_up(&ipc_futex);
437}
438
439
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).
445 *
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 *
450 */
451ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec, int flags)
452{
453 ipc_callid_t callid;
454
455 callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
456 /* Handle received answers */
457 if (callid & IPC_CALLID_ANSWERED) {
458 handle_answer(callid, call);
459 try_dispatch_queued_calls();
460 }
461
462 return callid;
463}
464
465/** Wait some time for an IPC call.
466 *
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.
471 *
472 * @return Hash of the answer.
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 {
479 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
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 *
487 * @param call Storage where the incoming call will be stored.
488 * @return Hash of the answer.
489 */
490ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
491{
492 ipc_callid_t callid;
493
494 do {
495 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
496 SYNCH_FLAGS_NON_BLOCKING);
497 } while (callid & IPC_CALLID_ANSWERED);
498
499 return callid;
500}
501
502/** Ask destination to do a callback connection.
503 *
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
508 * identifier of the phone that will be used for incoming
509 * calls. This identifier can be used for connection
510 * tracking.
511 *
512 * @return Zero on success or a negative error code.
513 */
514int ipc_connect_to_me(int phoneid, int arg1, int arg2, ipcarg_t *phonehash)
515{
516 return ipc_call_sync_3(phoneid, IPC_M_CONNECT_TO_ME, arg1, arg2, 0, 0, 0,
517 phonehash);
518}
519
520/** Ask through phone for a new connection to some service.
521 *
522 * @param phoneid Phone handle used for contacting the other side.
523 * @param arg1 User defined argument.
524 * @param arg2 User defined argument.
525 *
526 * @return New phone handle on success or a negative error code.
527 */
528int ipc_connect_me_to(int phoneid, int arg1, int arg2)
529{
530 ipcarg_t newphid;
531 int res;
532
533 res = ipc_call_sync_3(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, 0, 0, 0,
534 &newphid);
535 if (res)
536 return res;
537 return newphid;
538}
539
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 */
546int ipc_hangup(int phoneid)
547{
548 return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
549}
550
551/** Register IRQ notification.
552 *
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.
557 *
558 * @return Value returned by the kernel.
559 */
560int ipc_register_irq(int inr, int devno, int method, irq_code_t *ucode)
561{
562 return __SYSCALL4(SYS_IPC_REGISTER_IRQ, inr, devno, method,
563 (sysarg_t) ucode);
564}
565
566/** Unregister IRQ notification.
567 *
568 * @param inr IRQ number.
569 * @param devno Device number of the device generating inr.
570 *
571 * @return Value returned by the kernel.
572 */
573int ipc_unregister_irq(int inr, int devno)
574{
575 return __SYSCALL2(SYS_IPC_UNREGISTER_IRQ, inr, devno);
576}
577
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 */
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
596/** @}
597 */
Note: See TracBrowser for help on using the repository browser.