ipc.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Ondrej Palkovsky
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 /* queued_calls is protcted by async_futex, because if the
00076  * call cannot be sent into kernel, async framework is used
00077  * automatically
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                 /* Call asynchronous handler with error code */
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                         /* Async futex unlocked by previous call */
00173                 } else {
00174                         call->ptid = 0;
00175                         futex_up(&async_futex);
00176                 }
00177                 return;
00178         }
00179         call->u.callid = callid;
00180         /* Add call to list of dispatched calls */
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         /* We need to make sure that we get callid before
00203          * another thread accesses the queue again */
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         /* We need to make sure that we get callid before
00236          * another thread accesses the queue again */
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         /* TODO: integrate intelligently ipc_futex, so that it
00282          * is locked during ipc_call_async, until it is added
00283          * to dispatched_calls
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         /* Handle received answers */
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 /* Hang up specified phone */
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 

Generated on Sun Jun 18 18:00:18 2006 for HelenOS Userspace (ia64) by  doxygen 1.4.6