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
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#include <ipc/ipc.h>
30#include <libc.h>
31#include <malloc.h>
32#include <errno.h>
33#include <libadt/list.h>
34#include <stdio.h>
35#include <unistd.h>
36#include <futex.h>
37#include <kernel/synch/synch.h>
38#include <async.h>
39#include <psthread.h>
40
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 {
53 ipc_call_t data;
54 int phoneid;
55 } msg;
56 }u;
57 pstid_t ptid; /**< Thread waiting for sending this msg */
58} async_call_t;
59
60LIST_INITIALIZE(dispatched_calls);
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 */
68
69static atomic_t ipc_futex = FUTEX_INITIALIZER;
70
71int ipc_call_sync(int phoneid, ipcarg_t method, ipcarg_t arg1,
72 ipcarg_t *result)
73{
74 ipc_call_t resdata;
75 int callres;
76
77 callres = __SYSCALL4(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1,
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);
84}
85
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)
89{
90 ipc_call_t data;
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
98 callres = __SYSCALL3(SYS_IPC_CALL_SYNC, phoneid, (sysarg_t)&data,
99 (sysarg_t)&data);
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
112/** Syscall to send asynchronous message */
113static ipc_callid_t _ipc_call_async(int phoneid, ipc_call_t *data)
114{
115 return __SYSCALL2(SYS_IPC_CALL_ASYNC, phoneid, (sysarg_t)data);
116}
117
118/** Prolog to ipc_async_send functions */
119static inline async_call_t *ipc_prepare_async(void *private, ipc_async_callback_t callback)
120{
121 async_call_t *call;
122
123 call = malloc(sizeof(*call));
124 if (!call) {
125 if (callback)
126 callback(private, ENOMEM, NULL);
127 return NULL;
128 }
129 call->callback = callback;
130 call->private = private;
131
132 return call;
133}
134
135/** Epilogue of ipc_async_send functions */
136static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
137 async_call_t *call, int can_preempt)
138{
139 if (callid == IPC_CALLRET_FATAL) {
140 futex_up(&ipc_futex);
141 /* Call asynchronous handler with error code */
142 if (call->callback)
143 call->callback(call->private, ENOENT, NULL);
144 free(call);
145 return;
146 }
147
148 if (callid == IPC_CALLRET_TEMPORARY) {
149 futex_up(&ipc_futex);
150
151 call->u.msg.phoneid = phoneid;
152
153 futex_down(&async_futex);
154 list_append(&call->list, &queued_calls);
155
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 }
164 return;
165 }
166 call->u.callid = callid;
167 /* Add call to list of dispatched calls */
168 list_append(&call->list, &dispatched_calls);
169 futex_up(&ipc_futex);
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,
180 ipc_async_callback_t callback, int can_preempt)
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 }
199 ipc_finish_async(callid, phoneid, call, can_preempt);
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,
209 ipc_async_callback_t callback, int can_preempt)
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
227 ipc_finish_async(callid, phoneid, call, can_preempt);
228}
229
230
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,
244 ipcarg_t arg2)
245{
246 return __SYSCALL4(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2);
247}
248
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
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
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);
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);
279 if (callid == IPC_CALLRET_TEMPORARY) {
280 break;
281 }
282 list_remove(&call->list);
283
284 futex_up(&async_futex);
285 if (call->ptid)
286 psthread_add_ready(call->ptid);
287
288 if (callid == IPC_CALLRET_FATAL) {
289 if (call->callback)
290 call->callback(call->private, ENOENT, NULL);
291 free(call);
292 } else {
293 call->u.callid = callid;
294 futex_down(&ipc_futex);
295 list_append(&call->list, &dispatched_calls);
296 futex_up(&ipc_futex);
297 }
298 futex_down(&async_futex);
299 }
300 futex_up(&async_futex);
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 */
309static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
310{
311 link_t *item;
312 async_call_t *call;
313
314 callid &= ~IPC_CALLID_ANSWERED;
315
316 futex_down(&ipc_futex);
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);
322 futex_up(&ipc_futex);
323 if (call->callback)
324 call->callback(call->private,
325 IPC_GET_RETVAL(*data),
326 data);
327 free(call);
328 return;
329 }
330 }
331 futex_up(&ipc_futex);
332 printf("Received unidentified answer: %P!!!\n", callid);
333}
334
335
336/** One cycle of ipc wait for call call
337 *
338 * - dispatch ASYNC reoutines in the background
339 * @param call Space where the message is stored
340 * @param usec Timeout in microseconds
341 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking)
342 * @return Callid of the answer.
343 */
344ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec, int flags)
345{
346 ipc_callid_t callid;
347
348 callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
349 /* Handle received answers */
350 if (callid & IPC_CALLID_ANSWERED) {
351 handle_answer(callid, call);
352 try_dispatch_queued_calls();
353 }
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 {
370 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
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 {
387 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NON_BLOCKING);
388 } while (callid & IPC_CALLID_ANSWERED);
389
390 return callid;
391}
392
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)
398{
399 return ipc_call_sync_3(phoneid, IPC_M_CONNECT_TO_ME, arg1,
400 arg2, 0, 0, 0, phone);
401}
402
403/** Ask through phone for a new connection to some service
404 *
405 * @return new phoneid - OK, error code
406 */
407int ipc_connect_me_to(int phoneid, int arg1, int arg2)
408{
409 ipcarg_t newphid;
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;
417}
418
419/* Hang up specified phone */
420int ipc_hangup(int phoneid)
421{
422 return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
423}
424
425int ipc_register_irq(int irq, irq_code_t *ucode)
426{
427 return __SYSCALL2(SYS_IPC_REGISTER_IRQ, irq, (sysarg_t) ucode);
428}
429
430int ipc_unregister_irq(int irq)
431{
432 return __SYSCALL1(SYS_IPC_UNREGISTER_IRQ, irq);
433}
434
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.