source: mainline/libc/generic/ipc.c@ ce5bcb4

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ce5bcb4 was 085bd54, checked in by Ondrej Palkovsky <ondrap@…>, 19 years ago

Revised ipc. Now it is preferrable to use only functions from async.h, they
take care of correct buffering, waiting for answers etc.

  • Property mode set to 100644
File size: 11.4 KB
RevLine 
[b419162]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
[7ee6aff]29#include <ipc/ipc.h>
[b419162]30#include <libc.h>
[936351c1]31#include <malloc.h>
32#include <errno.h>
[7ee6aff]33#include <libadt/list.h>
[936351c1]34#include <stdio.h>
35#include <unistd.h>
[35509652]36#include <futex.h>
[04a73cdf]37#include <kernel/synch/synch.h>
[fc42b28]38#include <async.h>
39#include <psthread.h>
[b419162]40
[936351c1]41/** Structure used for keeping track of sent async msgs
42 * and queing unsent msgs
43 *
44 */
45typedef struct {
46 link_t list;
47
48 ipc_async_callback_t callback;
49 void *private;
50 union {
51 ipc_callid_t callid;
52 struct {
[4c61e60]53 ipc_call_t data;
[936351c1]54 int phoneid;
55 } msg;
56 }u;
[fc42b28]57 pstid_t ptid; /**< Thread waiting for sending this msg */
[936351c1]58} async_call_t;
59
60LIST_INITIALIZE(dispatched_calls);
[fc42b28]61
62/* queued_calls is protcted by async_futex, because if the
63 * call cannot be sent into kernel, async framework is used
64 * automatically
65 */
66LIST_INITIALIZE(queued_calls); /**< List of async calls that were not accepted
67 * by kernel */
[936351c1]68
[80649a91]69static atomic_t ipc_futex = FUTEX_INITIALIZER;
[35509652]70
[936351c1]71int ipc_call_sync(int phoneid, ipcarg_t method, ipcarg_t arg1,
72 ipcarg_t *result)
[b419162]73{
[4c61e60]74 ipc_call_t resdata;
[06502f7d]75 int callres;
76
[936351c1]77 callres = __SYSCALL4(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1,
[06502f7d]78 (sysarg_t)&resdata);
79 if (callres)
80 return callres;
81 if (result)
82 *result = IPC_GET_ARG1(resdata);
83 return IPC_GET_RETVAL(resdata);
[b419162]84}
85
[936351c1]86int ipc_call_sync_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
87 ipcarg_t arg2, ipcarg_t arg3,
88 ipcarg_t *result1, ipcarg_t *result2, ipcarg_t *result3)
[06502f7d]89{
[4c61e60]90 ipc_call_t data;
[06502f7d]91 int callres;
92
93 IPC_SET_METHOD(data, method);
94 IPC_SET_ARG1(data, arg1);
95 IPC_SET_ARG2(data, arg2);
96 IPC_SET_ARG3(data, arg3);
97
[d73942c]98 callres = __SYSCALL3(SYS_IPC_CALL_SYNC, phoneid, (sysarg_t)&data,
99 (sysarg_t)&data);
[06502f7d]100 if (callres)
101 return callres;
102
103 if (result1)
104 *result1 = IPC_GET_ARG1(data);
105 if (result2)
106 *result2 = IPC_GET_ARG2(data);
107 if (result3)
108 *result3 = IPC_GET_ARG3(data);
109 return IPC_GET_RETVAL(data);
110}
111
[936351c1]112/** Syscall to send asynchronous message */
[4c61e60]113static ipc_callid_t _ipc_call_async(int phoneid, ipc_call_t *data)
[936351c1]114{
115 return __SYSCALL2(SYS_IPC_CALL_ASYNC, phoneid, (sysarg_t)data);
116}
117
[c1d2c9d]118/** Prolog to ipc_async_send functions */
119static inline async_call_t *ipc_prepare_async(void *private, ipc_async_callback_t callback)
[b419162]120{
[936351c1]121 async_call_t *call;
[06502f7d]122
[936351c1]123 call = malloc(sizeof(*call));
124 if (!call) {
[a784a96]125 if (callback)
126 callback(private, ENOMEM, NULL);
[c1d2c9d]127 return NULL;
[936351c1]128 }
[fc42b28]129 call->callback = callback;
130 call->private = private;
131
[c1d2c9d]132 return call;
133}
134
135/** Epilogue of ipc_async_send functions */
[b1f51f0]136static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
137 async_call_t *call, int can_preempt)
[c1d2c9d]138{
[06502f7d]139 if (callid == IPC_CALLRET_FATAL) {
[fc42b28]140 futex_up(&ipc_futex);
[06502f7d]141 /* Call asynchronous handler with error code */
[c1d2c9d]142 if (call->callback)
143 call->callback(call->private, ENOENT, NULL);
[936351c1]144 free(call);
[06502f7d]145 return;
146 }
[936351c1]147
[06502f7d]148 if (callid == IPC_CALLRET_TEMPORARY) {
[fc42b28]149 futex_up(&ipc_futex);
150
[936351c1]151 call->u.msg.phoneid = phoneid;
[b1f51f0]152
[fc42b28]153 futex_down(&async_futex);
[936351c1]154 list_append(&call->list, &queued_calls);
[fc42b28]155
[b1f51f0]156 if (can_preempt) {
157 call->ptid = psthread_get_id();
158 psthread_schedule_next_adv(PS_TO_MANAGER);
159 /* Async futex unlocked by previous call */
160 } else {
161 call->ptid = 0;
162 futex_up(&async_futex);
163 }
[06502f7d]164 return;
165 }
[936351c1]166 call->u.callid = callid;
167 /* Add call to list of dispatched calls */
168 list_append(&call->list, &dispatched_calls);
[35509652]169 futex_up(&ipc_futex);
[c1d2c9d]170
171}
172
173/** Send asynchronous message
174 *
175 * - if fatal error, call callback handler with proper error code
176 * - if message cannot be temporarily sent, add to queue
177 */
178void ipc_call_async_2(int phoneid, ipcarg_t method, ipcarg_t arg1,
179 ipcarg_t arg2, void *private,
[b1f51f0]180 ipc_async_callback_t callback, int can_preempt)
[c1d2c9d]181{
182 async_call_t *call;
183 ipc_callid_t callid;
184
185 call = ipc_prepare_async(private, callback);
186 if (!call)
187 return;
188
189 /* We need to make sure that we get callid before
190 * another thread accesses the queue again */
191 futex_down(&ipc_futex);
192 callid = __SYSCALL4(SYS_IPC_CALL_ASYNC_FAST, phoneid, method, arg1, arg2);
193
194 if (callid == IPC_CALLRET_TEMPORARY) {
195 IPC_SET_METHOD(call->u.msg.data, method);
196 IPC_SET_ARG1(call->u.msg.data, arg1);
197 IPC_SET_ARG2(call->u.msg.data, arg2);
198 }
[b1f51f0]199 ipc_finish_async(callid, phoneid, call, can_preempt);
[c1d2c9d]200}
201
202/** Send asynchronous message
203 *
204 * - if fatal error, call callback handler with proper error code
205 * - if message cannot be temporarily sent, add to queue
206 */
207void ipc_call_async_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
208 ipcarg_t arg2, ipcarg_t arg3, void *private,
[b1f51f0]209 ipc_async_callback_t callback, int can_preempt)
[c1d2c9d]210{
211 async_call_t *call;
212 ipc_callid_t callid;
213
214 call = ipc_prepare_async(private, callback);
215 if (!call)
216 return;
217
218 IPC_SET_METHOD(call->u.msg.data, method);
219 IPC_SET_ARG1(call->u.msg.data, arg1);
220 IPC_SET_ARG2(call->u.msg.data, arg2);
221 IPC_SET_ARG3(call->u.msg.data, arg3);
222 /* We need to make sure that we get callid before
223 * another thread accesses the queue again */
224 futex_down(&ipc_futex);
225 callid = _ipc_call_async(phoneid, &call->u.msg.data);
226
[b1f51f0]227 ipc_finish_async(callid, phoneid, call, can_preempt);
[b419162]228}
229
[06502f7d]230
[250717cc]231/** Send a fast answer to a received call.
232 *
233 * The fast answer makes use of passing retval and first two arguments in registers.
234 * If you need to return more, use the ipc_answer() instead.
235 *
236 * @param callid ID of the call being answered.
237 * @param retval Return value.
238 * @param arg1 First return argument.
239 * @param arg2 Second return argument.
240 *
241 * @return Zero on success or a value from @ref errno.h on failure.
242 */
243ipcarg_t ipc_answer_fast(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1,
[936351c1]244 ipcarg_t arg2)
[b419162]245{
[8a568e3]246 return __SYSCALL4(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2);
[b419162]247}
[06502f7d]248
[250717cc]249/** Send a full answer to a received call.
250 *
251 * @param callid ID of the call being answered.
252 * @param call Call data. Must be already initialized by the responder.
253 *
254 * @return Zero on success or a value from @ref errno.h on failure.
255 */
256ipcarg_t ipc_answer(ipc_callid_t callid, ipc_call_t *call)
257{
258 return __SYSCALL2(SYS_IPC_ANSWER, callid, (sysarg_t) call);
259}
260
261
[936351c1]262/** Try to dispatch queed calls from async queue */
263static void try_dispatch_queued_calls(void)
264{
265 async_call_t *call;
266 ipc_callid_t callid;
267
[fc42b28]268 /* TODO: integrate intelligently ipc_futex, so that it
269 * is locked during ipc_call_async, until it is added
270 * to dispatched_calls
271 */
272 futex_down(&async_futex);
[936351c1]273 while (!list_empty(&queued_calls)) {
274 call = list_get_instance(queued_calls.next, async_call_t,
275 list);
276
277 callid = _ipc_call_async(call->u.msg.phoneid,
278 &call->u.msg.data);
[fc42b28]279 if (callid == IPC_CALLRET_TEMPORARY) {
[936351c1]280 break;
[fc42b28]281 }
[936351c1]282 list_remove(&call->list);
[35509652]283
[fc42b28]284 futex_up(&async_futex);
[b1f51f0]285 if (call->ptid)
286 psthread_add_ready(call->ptid);
[fc42b28]287
[936351c1]288 if (callid == IPC_CALLRET_FATAL) {
[a784a96]289 if (call->callback)
290 call->callback(call->private, ENOENT, NULL);
[936351c1]291 free(call);
292 } else {
293 call->u.callid = callid;
[fc42b28]294 futex_down(&ipc_futex);
[936351c1]295 list_append(&call->list, &dispatched_calls);
[fc42b28]296 futex_up(&ipc_futex);
[936351c1]297 }
[fc42b28]298 futex_down(&async_futex);
[936351c1]299 }
[fc42b28]300 futex_up(&async_futex);
[936351c1]301}
302
303/** Handle received answer
304 *
305 * TODO: Make it use hash table
306 *
307 * @param callid Callid (with first bit set) of the answered call
308 */
[4c61e60]309static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
[936351c1]310{
311 link_t *item;
312 async_call_t *call;
313
314 callid &= ~IPC_CALLID_ANSWERED;
315
[35509652]316 futex_down(&ipc_futex);
[936351c1]317 for (item = dispatched_calls.next; item != &dispatched_calls;
318 item = item->next) {
319 call = list_get_instance(item, async_call_t, list);
320 if (call->u.callid == callid) {
321 list_remove(&call->list);
[35509652]322 futex_up(&ipc_futex);
[a784a96]323 if (call->callback)
324 call->callback(call->private,
325 IPC_GET_RETVAL(*data),
326 data);
327 free(call);
[936351c1]328 return;
329 }
330 }
[35509652]331 futex_up(&ipc_futex);
[936351c1]332 printf("Received unidentified answer: %P!!!\n", callid);
333}
334
335
[80649a91]336/** One cycle of ipc wait for call call
[b419162]337 *
338 * - dispatch ASYNC reoutines in the background
[04a73cdf]339 * @param call Space where the message is stored
[80649a91]340 * @param usec Timeout in microseconds
341 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking)
[04a73cdf]342 * @return Callid of the answer.
[b419162]343 */
[80649a91]344ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec, int flags)
[b419162]345{
346 ipc_callid_t callid;
347
[80649a91]348 callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
349 /* Handle received answers */
[fc42b28]350 if (callid & IPC_CALLID_ANSWERED) {
[80649a91]351 handle_answer(callid, call);
[fc42b28]352 try_dispatch_queued_calls();
353 }
[04a73cdf]354
355 return callid;
356}
357
358/** Wait some time for an IPC call.
359 *
360 * - dispatch ASYNC reoutines in the background
361 * @param call Space where the message is stored
362 * @param usec Timeout in microseconds.
363 * @return Callid of the answer.
364 */
365ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, uint32_t usec)
366{
367 ipc_callid_t callid;
368
369 do {
[2d22049]370 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
[04a73cdf]371 } while (callid & IPC_CALLID_ANSWERED);
372
373 return callid;
374}
375
376/** Check if there is an IPC call waiting to be picked up.
377 *
378 * - dispatch ASYNC reoutines in the background
379 * @param call Space where the message is stored
380 * @return Callid of the answer.
381 */
382ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
383{
384 ipc_callid_t callid;
385
386 do {
[2d22049]387 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NON_BLOCKING);
[06502f7d]388 } while (callid & IPC_CALLID_ANSWERED);
[936351c1]389
[b419162]390 return callid;
391}
[5106e98]392
[4c61e60]393/** Ask destination to do a callback connection
394 *
395 * @return 0 - OK, error code
396 */
397int ipc_connect_to_me(int phoneid, int arg1, int arg2, ipcarg_t *phone)
[5106e98]398{
[4c61e60]399 return ipc_call_sync_3(phoneid, IPC_M_CONNECT_TO_ME, arg1,
400 arg2, 0, 0, 0, phone);
[5106e98]401}
[11eae82]402
[4c61e60]403/** Ask through phone for a new connection to some service
404 *
405 * @return new phoneid - OK, error code
406 */
[11eae82]407int ipc_connect_me_to(int phoneid, int arg1, int arg2)
408{
[06b0d112]409 ipcarg_t newphid;
[4c61e60]410 int res;
411
412 res = ipc_call_sync_3(phoneid, IPC_M_CONNECT_ME_TO, arg1,
413 arg2, 0, 0, 0, &newphid);
414 if (res)
415 return res;
416 return newphid;
[11eae82]417}
418
[7048773]419/* Hang up specified phone */
420int ipc_hangup(int phoneid)
421{
422 return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
423}
[6180b57]424
[602ca36b]425int ipc_register_irq(int irq, irq_code_t *ucode)
[6180b57]426{
[602ca36b]427 return __SYSCALL2(SYS_IPC_REGISTER_IRQ, irq, (sysarg_t) ucode);
[6180b57]428}
429
430int ipc_unregister_irq(int irq)
431{
432 return __SYSCALL1(SYS_IPC_UNREGISTER_IRQ, irq);
433}
[8a568e3]434
[043dcc27]435int ipc_forward_fast(ipc_callid_t callid, int phoneid, int method, ipcarg_t arg1)
436{
437 return __SYSCALL4(SYS_IPC_FORWARD_FAST, callid, phoneid, method, arg1);
438}
439
Note: See TracBrowser for help on using the repository browser.