00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00042 #include <ipc/ipc.h>
00043 #include <libc.h>
00044 #include <malloc.h>
00045 #include <errno.h>
00046 #include <libadt/list.h>
00047 #include <stdio.h>
00048 #include <unistd.h>
00049 #include <futex.h>
00050 #include <kernel/synch/synch.h>
00051 #include <async.h>
00052 #include <psthread.h>
00053
00058 typedef struct {
00059 link_t list;
00060
00061 ipc_async_callback_t callback;
00062 void *private;
00063 union {
00064 ipc_callid_t callid;
00065 struct {
00066 ipc_call_t data;
00067 int phoneid;
00068 } msg;
00069 }u;
00070 pstid_t ptid;
00071 } async_call_t;
00072
00073 LIST_INITIALIZE(dispatched_calls);
00074
00075
00076
00077
00078
00079 LIST_INITIALIZE(queued_calls);
00082 static atomic_t ipc_futex = FUTEX_INITIALIZER;
00083
00084 int ipc_call_sync(int phoneid, ipcarg_t method, ipcarg_t arg1,
00085 ipcarg_t *result)
00086 {
00087 ipc_call_t resdata;
00088 int callres;
00089
00090 callres = __SYSCALL4(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1,
00091 (sysarg_t)&resdata);
00092 if (callres)
00093 return callres;
00094 if (result)
00095 *result = IPC_GET_ARG1(resdata);
00096 return IPC_GET_RETVAL(resdata);
00097 }
00098
00099 int ipc_call_sync_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
00100 ipcarg_t arg2, ipcarg_t arg3,
00101 ipcarg_t *result1, ipcarg_t *result2, ipcarg_t *result3)
00102 {
00103 ipc_call_t data;
00104 int callres;
00105
00106 IPC_SET_METHOD(data, method);
00107 IPC_SET_ARG1(data, arg1);
00108 IPC_SET_ARG2(data, arg2);
00109 IPC_SET_ARG3(data, arg3);
00110
00111 callres = __SYSCALL3(SYS_IPC_CALL_SYNC, phoneid, (sysarg_t)&data,
00112 (sysarg_t)&data);
00113 if (callres)
00114 return callres;
00115
00116 if (result1)
00117 *result1 = IPC_GET_ARG1(data);
00118 if (result2)
00119 *result2 = IPC_GET_ARG2(data);
00120 if (result3)
00121 *result3 = IPC_GET_ARG3(data);
00122 return IPC_GET_RETVAL(data);
00123 }
00124
00126 static ipc_callid_t _ipc_call_async(int phoneid, ipc_call_t *data)
00127 {
00128 return __SYSCALL2(SYS_IPC_CALL_ASYNC, phoneid, (sysarg_t)data);
00129 }
00130
00132 static inline async_call_t *ipc_prepare_async(void *private, ipc_async_callback_t callback)
00133 {
00134 async_call_t *call;
00135
00136 call = malloc(sizeof(*call));
00137 if (!call) {
00138 if (callback)
00139 callback(private, ENOMEM, NULL);
00140 return NULL;
00141 }
00142 call->callback = callback;
00143 call->private = private;
00144
00145 return call;
00146 }
00147
00149 static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
00150 async_call_t *call, int can_preempt)
00151 {
00152 if (callid == IPC_CALLRET_FATAL) {
00153 futex_up(&ipc_futex);
00154
00155 if (call->callback)
00156 call->callback(call->private, ENOENT, NULL);
00157 free(call);
00158 return;
00159 }
00160
00161 if (callid == IPC_CALLRET_TEMPORARY) {
00162 futex_up(&ipc_futex);
00163
00164 call->u.msg.phoneid = phoneid;
00165
00166 futex_down(&async_futex);
00167 list_append(&call->list, &queued_calls);
00168
00169 if (can_preempt) {
00170 call->ptid = psthread_get_id();
00171 psthread_schedule_next_adv(PS_TO_MANAGER);
00172
00173 } else {
00174 call->ptid = 0;
00175 futex_up(&async_futex);
00176 }
00177 return;
00178 }
00179 call->u.callid = callid;
00180
00181 list_append(&call->list, &dispatched_calls);
00182 futex_up(&ipc_futex);
00183
00184 }
00185
00191 void ipc_call_async_2(int phoneid, ipcarg_t method, ipcarg_t arg1,
00192 ipcarg_t arg2, void *private,
00193 ipc_async_callback_t callback, int can_preempt)
00194 {
00195 async_call_t *call;
00196 ipc_callid_t callid;
00197
00198 call = ipc_prepare_async(private, callback);
00199 if (!call)
00200 return;
00201
00202
00203
00204 futex_down(&ipc_futex);
00205 callid = __SYSCALL4(SYS_IPC_CALL_ASYNC_FAST, phoneid, method, arg1, arg2);
00206
00207 if (callid == IPC_CALLRET_TEMPORARY) {
00208 IPC_SET_METHOD(call->u.msg.data, method);
00209 IPC_SET_ARG1(call->u.msg.data, arg1);
00210 IPC_SET_ARG2(call->u.msg.data, arg2);
00211 }
00212 ipc_finish_async(callid, phoneid, call, can_preempt);
00213 }
00214
00220 void ipc_call_async_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
00221 ipcarg_t arg2, ipcarg_t arg3, void *private,
00222 ipc_async_callback_t callback, int can_preempt)
00223 {
00224 async_call_t *call;
00225 ipc_callid_t callid;
00226
00227 call = ipc_prepare_async(private, callback);
00228 if (!call)
00229 return;
00230
00231 IPC_SET_METHOD(call->u.msg.data, method);
00232 IPC_SET_ARG1(call->u.msg.data, arg1);
00233 IPC_SET_ARG2(call->u.msg.data, arg2);
00234 IPC_SET_ARG3(call->u.msg.data, arg3);
00235
00236
00237 futex_down(&ipc_futex);
00238 callid = _ipc_call_async(phoneid, &call->u.msg.data);
00239
00240 ipc_finish_async(callid, phoneid, call, can_preempt);
00241 }
00242
00243
00256 ipcarg_t ipc_answer_fast(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1,
00257 ipcarg_t arg2)
00258 {
00259 return __SYSCALL4(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2);
00260 }
00261
00269 ipcarg_t ipc_answer(ipc_callid_t callid, ipc_call_t *call)
00270 {
00271 return __SYSCALL2(SYS_IPC_ANSWER, callid, (sysarg_t) call);
00272 }
00273
00274
00276 static void try_dispatch_queued_calls(void)
00277 {
00278 async_call_t *call;
00279 ipc_callid_t callid;
00280
00281
00282
00283
00284
00285 futex_down(&async_futex);
00286 while (!list_empty(&queued_calls)) {
00287 call = list_get_instance(queued_calls.next, async_call_t,
00288 list);
00289
00290 callid = _ipc_call_async(call->u.msg.phoneid,
00291 &call->u.msg.data);
00292 if (callid == IPC_CALLRET_TEMPORARY) {
00293 break;
00294 }
00295 list_remove(&call->list);
00296
00297 futex_up(&async_futex);
00298 if (call->ptid)
00299 psthread_add_ready(call->ptid);
00300
00301 if (callid == IPC_CALLRET_FATAL) {
00302 if (call->callback)
00303 call->callback(call->private, ENOENT, NULL);
00304 free(call);
00305 } else {
00306 call->u.callid = callid;
00307 futex_down(&ipc_futex);
00308 list_append(&call->list, &dispatched_calls);
00309 futex_up(&ipc_futex);
00310 }
00311 futex_down(&async_futex);
00312 }
00313 futex_up(&async_futex);
00314 }
00315
00322 static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
00323 {
00324 link_t *item;
00325 async_call_t *call;
00326
00327 callid &= ~IPC_CALLID_ANSWERED;
00328
00329 futex_down(&ipc_futex);
00330 for (item = dispatched_calls.next; item != &dispatched_calls;
00331 item = item->next) {
00332 call = list_get_instance(item, async_call_t, list);
00333 if (call->u.callid == callid) {
00334 list_remove(&call->list);
00335 futex_up(&ipc_futex);
00336 if (call->callback)
00337 call->callback(call->private,
00338 IPC_GET_RETVAL(*data),
00339 data);
00340 free(call);
00341 return;
00342 }
00343 }
00344 futex_up(&ipc_futex);
00345 printf("Received unidentified answer: %P!!!\n", callid);
00346 }
00347
00348
00357 ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec, int flags)
00358 {
00359 ipc_callid_t callid;
00360
00361 callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
00362
00363 if (callid & IPC_CALLID_ANSWERED) {
00364 handle_answer(callid, call);
00365 try_dispatch_queued_calls();
00366 }
00367
00368 return callid;
00369 }
00370
00379 ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, uint32_t usec)
00380 {
00381 ipc_callid_t callid;
00382
00383 do {
00384 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
00385 } while (callid & IPC_CALLID_ANSWERED);
00386
00387 return callid;
00388 }
00389
00397 ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
00398 {
00399 ipc_callid_t callid;
00400
00401 do {
00402 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NON_BLOCKING);
00403 } while (callid & IPC_CALLID_ANSWERED);
00404
00405 return callid;
00406 }
00407
00412 int ipc_connect_to_me(int phoneid, int arg1, int arg2, ipcarg_t *phone)
00413 {
00414 return ipc_call_sync_3(phoneid, IPC_M_CONNECT_TO_ME, arg1,
00415 arg2, 0, 0, 0, phone);
00416 }
00417
00422 int ipc_connect_me_to(int phoneid, int arg1, int arg2)
00423 {
00424 ipcarg_t newphid;
00425 int res;
00426
00427 res = ipc_call_sync_3(phoneid, IPC_M_CONNECT_ME_TO, arg1,
00428 arg2, 0, 0, 0, &newphid);
00429 if (res)
00430 return res;
00431 return newphid;
00432 }
00433
00434
00435 int ipc_hangup(int phoneid)
00436 {
00437 return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
00438 }
00439
00440 int ipc_register_irq(int irq, irq_code_t *ucode)
00441 {
00442 return __SYSCALL2(SYS_IPC_REGISTER_IRQ, irq, (sysarg_t) ucode);
00443 }
00444
00445 int ipc_unregister_irq(int irq)
00446 {
00447 return __SYSCALL1(SYS_IPC_UNREGISTER_IRQ, irq);
00448 }
00449
00450 int ipc_forward_fast(ipc_callid_t callid, int phoneid, int method, ipcarg_t arg1)
00451 {
00452 return __SYSCALL4(SYS_IPC_FORWARD_FAST, callid, phoneid, method, arg1);
00453 }
00454
00455
00456