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 <adt/list.h>
00047 #include <futex.h>
00048 #include <fibril.h>
00049 
00054 typedef struct {
00055         link_t list;
00056         
00057         ipc_async_callback_t callback;
00058         void *private;
00059         
00060         union {
00061                 ipc_callid_t callid;
00062                 struct {
00063                         ipc_call_t data;
00064                         int phoneid;
00065                 } msg;
00066         } u;
00067         
00069         fid_t fid;
00070 } async_call_t;
00071 
00072 LIST_INITIALIZE(dispatched_calls);
00073 
00080 LIST_INITIALIZE(queued_calls);
00081 
00082 static atomic_t ipc_futex = FUTEX_INITIALIZER;
00083 
00105 int ipc_call_sync_fast(int phoneid, sysarg_t method, sysarg_t arg1,
00106     sysarg_t arg2, sysarg_t arg3, sysarg_t *result1, sysarg_t *result2,
00107     sysarg_t *result3, sysarg_t *result4, sysarg_t *result5)
00108 {
00109         ipc_call_t resdata;
00110         int callres = __SYSCALL6(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1,
00111             arg2, arg3, (sysarg_t) &resdata);
00112         if (callres)
00113                 return callres;
00114         
00115         if (result1)
00116                 *result1 = IPC_GET_ARG1(resdata);
00117         if (result2)
00118                 *result2 = IPC_GET_ARG2(resdata);
00119         if (result3)
00120                 *result3 = IPC_GET_ARG3(resdata);
00121         if (result4)
00122                 *result4 = IPC_GET_ARG4(resdata);
00123         if (result5)
00124                 *result5 = IPC_GET_ARG5(resdata);
00125         
00126         return IPC_GET_RETVAL(resdata);
00127 }
00128 
00148 int ipc_call_sync_slow(int phoneid, sysarg_t imethod, sysarg_t arg1,
00149     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
00150     sysarg_t *result1, sysarg_t *result2, sysarg_t *result3, sysarg_t *result4,
00151     sysarg_t *result5)
00152 {
00153         ipc_call_t data;
00154         
00155         IPC_SET_IMETHOD(data, imethod);
00156         IPC_SET_ARG1(data, arg1);
00157         IPC_SET_ARG2(data, arg2);
00158         IPC_SET_ARG3(data, arg3);
00159         IPC_SET_ARG4(data, arg4);
00160         IPC_SET_ARG5(data, arg5);
00161         
00162         int callres = __SYSCALL3(SYS_IPC_CALL_SYNC_SLOW, phoneid,
00163             (sysarg_t) &data, (sysarg_t) &data);
00164         if (callres)
00165                 return callres;
00166         
00167         if (result1)
00168                 *result1 = IPC_GET_ARG1(data);
00169         if (result2)
00170                 *result2 = IPC_GET_ARG2(data);
00171         if (result3)
00172                 *result3 = IPC_GET_ARG3(data);
00173         if (result4)
00174                 *result4 = IPC_GET_ARG4(data);
00175         if (result5)
00176                 *result5 = IPC_GET_ARG5(data);
00177         
00178         return IPC_GET_RETVAL(data);
00179 }
00180 
00189 static ipc_callid_t ipc_call_async_internal(int phoneid, ipc_call_t *data)
00190 {
00191         return __SYSCALL2(SYS_IPC_CALL_ASYNC_SLOW, phoneid, (sysarg_t) data);
00192 }
00193 
00202 static inline async_call_t *ipc_prepare_async(void *private,
00203     ipc_async_callback_t callback)
00204 {
00205         async_call_t *call =
00206             (async_call_t *) malloc(sizeof(async_call_t));
00207         if (!call) {
00208                 if (callback)
00209                         callback(private, ENOMEM, NULL);
00210                 
00211                 return NULL;
00212         }
00213         
00214         call->callback = callback;
00215         call->private = private;
00216         
00217         return call;
00218 }
00219 
00229 static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
00230     async_call_t *call, bool can_preempt)
00231 {
00232         if (!call) {
00233                 /* Nothing to do regardless if failed or not */
00234                 futex_up(&ipc_futex);
00235                 return;
00236         }
00237         
00238         if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
00239                 futex_up(&ipc_futex);
00240                 
00241                 /* Call asynchronous handler with error code */
00242                 if (call->callback)
00243                         call->callback(call->private, ENOENT, NULL);
00244                 
00245                 free(call);
00246                 return;
00247         }
00248         
00249         if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) {
00250                 futex_up(&ipc_futex);
00251                 
00252                 call->u.msg.phoneid = phoneid;
00253                 
00254                 futex_down(&async_futex);
00255                 list_append(&call->list, &queued_calls);
00256                 
00257                 if (can_preempt) {
00258                         call->fid = fibril_get_id();
00259                         fibril_switch(FIBRIL_TO_MANAGER);
00260                         /* Async futex unlocked by previous call */
00261                 } else {
00262                         call->fid = 0;
00263                         futex_up(&async_futex);
00264                 }
00265                 
00266                 return;
00267         }
00268         
00269         call->u.callid = callid;
00270         
00271         /* Add call to the list of dispatched calls */
00272         list_append(&call->list, &dispatched_calls);
00273         futex_up(&ipc_futex);
00274 }
00275 
00300 void ipc_call_async_fast(int phoneid, sysarg_t imethod, sysarg_t arg1,
00301     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, void *private,
00302     ipc_async_callback_t callback, bool can_preempt)
00303 {
00304         async_call_t *call = NULL;
00305         
00306         if (callback) {
00307                 call = ipc_prepare_async(private, callback);
00308                 if (!call)
00309                         return;
00310         }
00311         
00312         /*
00313          * We need to make sure that we get callid
00314          * before another thread accesses the queue again.
00315          */
00316         
00317         futex_down(&ipc_futex);
00318         ipc_callid_t callid = __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phoneid,
00319             imethod, arg1, arg2, arg3, arg4);
00320         
00321         if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) {
00322                 if (!call) {
00323                         call = ipc_prepare_async(private, callback);
00324                         if (!call)
00325                                 return;
00326                 }
00327                 
00328                 IPC_SET_IMETHOD(call->u.msg.data, imethod);
00329                 IPC_SET_ARG1(call->u.msg.data, arg1);
00330                 IPC_SET_ARG2(call->u.msg.data, arg2);
00331                 IPC_SET_ARG3(call->u.msg.data, arg3);
00332                 IPC_SET_ARG4(call->u.msg.data, arg4);
00333                 
00334                 /*
00335                  * To achieve deterministic behavior, we always zero out the
00336                  * arguments that are beyond the limits of the fast version.
00337                  */
00338                 
00339                 IPC_SET_ARG5(call->u.msg.data, 0);
00340         }
00341         
00342         ipc_finish_async(callid, phoneid, call, can_preempt);
00343 }
00344 
00367 void ipc_call_async_slow(int phoneid, sysarg_t imethod, sysarg_t arg1,
00368     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, void *private,
00369     ipc_async_callback_t callback, bool can_preempt)
00370 {
00371         async_call_t *call = ipc_prepare_async(private, callback);
00372         if (!call)
00373                 return;
00374         
00375         IPC_SET_IMETHOD(call->u.msg.data, imethod);
00376         IPC_SET_ARG1(call->u.msg.data, arg1);
00377         IPC_SET_ARG2(call->u.msg.data, arg2);
00378         IPC_SET_ARG3(call->u.msg.data, arg3);
00379         IPC_SET_ARG4(call->u.msg.data, arg4);
00380         IPC_SET_ARG5(call->u.msg.data, arg5);
00381         
00382         /*
00383          * We need to make sure that we get callid
00384          * before another threadaccesses the queue again.
00385          */
00386         
00387         futex_down(&ipc_futex);
00388         ipc_callid_t callid =
00389             ipc_call_async_internal(phoneid, &call->u.msg.data);
00390         
00391         ipc_finish_async(callid, phoneid, call, can_preempt);
00392 }
00393 
00410 sysarg_t ipc_answer_fast(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
00411     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
00412 {
00413         return __SYSCALL6(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2, arg3,
00414             arg4);
00415 }
00416 
00431 sysarg_t ipc_answer_slow(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
00432     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
00433 {
00434         ipc_call_t data;
00435         
00436         IPC_SET_RETVAL(data, retval);
00437         IPC_SET_ARG1(data, arg1);
00438         IPC_SET_ARG2(data, arg2);
00439         IPC_SET_ARG3(data, arg3);
00440         IPC_SET_ARG4(data, arg4);
00441         IPC_SET_ARG5(data, arg5);
00442         
00443         return __SYSCALL2(SYS_IPC_ANSWER_SLOW, callid, (sysarg_t) &data);
00444 }
00445 
00449 static void dispatch_queued_calls(void)
00450 {
00456         futex_down(&async_futex);
00457         
00458         while (!list_empty(&queued_calls)) {
00459                 async_call_t *call =
00460                     list_get_instance(queued_calls.next, async_call_t, list);
00461                 ipc_callid_t callid =
00462                     ipc_call_async_internal(call->u.msg.phoneid, &call->u.msg.data);
00463                 
00464                 if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY)
00465                         break;
00466                 
00467                 list_remove(&call->list);
00468                 
00469                 futex_up(&async_futex);
00470                 
00471                 if (call->fid)
00472                         fibril_add_ready(call->fid);
00473                 
00474                 if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
00475                         if (call->callback)
00476                                 call->callback(call->private, ENOENT, NULL);
00477                         
00478                         free(call);
00479                 } else {
00480                         call->u.callid = callid;
00481                         
00482                         futex_down(&ipc_futex);
00483                         list_append(&call->list, &dispatched_calls);
00484                         futex_up(&ipc_futex);
00485                 }
00486                 
00487                 futex_down(&async_futex);
00488         }
00489         
00490         futex_up(&async_futex);
00491 }
00492 
00506 static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
00507 {
00508         callid &= ~IPC_CALLID_ANSWERED;
00509         
00510         futex_down(&ipc_futex);
00511         
00512         link_t *item;
00513         for (item = dispatched_calls.next; item != &dispatched_calls;
00514             item = item->next) {
00515                 async_call_t *call =
00516                     list_get_instance(item, async_call_t, list);
00517                 
00518                 if (call->u.callid == callid) {
00519                         list_remove(&call->list);
00520                         
00521                         futex_up(&ipc_futex);
00522                         
00523                         if (call->callback)
00524                                 call->callback(call->private,
00525                                     IPC_GET_RETVAL(*data), data);
00526                         
00527                         free(call);
00528                         return;
00529                 }
00530         }
00531         
00532         futex_up(&ipc_futex);
00533 }
00534 
00546 ipc_callid_t ipc_wait_cycle(ipc_call_t *call, sysarg_t usec,
00547     unsigned int flags)
00548 {
00549         ipc_callid_t callid =
00550             __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
00551         
00552         /* Handle received answers */
00553         if (callid & IPC_CALLID_ANSWERED) {
00554                 handle_answer(callid, call);
00555                 dispatch_queued_calls();
00556         }
00557         
00558         return callid;
00559 }
00560 
00564 void ipc_poke(void)
00565 {
00566         __SYSCALL0(SYS_IPC_POKE);
00567 }
00568 
00579 ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, sysarg_t usec)
00580 {
00581         ipc_callid_t callid;
00582         
00583         do {
00584                 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
00585         } while (callid & IPC_CALLID_ANSWERED);
00586         
00587         return callid;
00588 }
00589 
00599 ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
00600 {
00601         ipc_callid_t callid;
00602         
00603         do {
00604                 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
00605                     SYNCH_FLAGS_NON_BLOCKING);
00606         } while (callid & IPC_CALLID_ANSWERED);
00607         
00608         return callid;
00609 }
00610 
00627 int ipc_connect_to_me(int phoneid, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
00628     sysarg_t *taskhash, sysarg_t *phonehash)
00629 {
00630         return ipc_call_sync_3_5(phoneid, IPC_M_CONNECT_TO_ME, arg1, arg2,
00631             arg3, NULL, NULL, NULL, taskhash, phonehash);
00632 }
00633 
00644 int ipc_connect_me_to(int phoneid, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
00645 {
00646         sysarg_t newphid;
00647         int res = ipc_call_sync_3_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3,
00648             NULL, NULL, NULL, NULL, &newphid);
00649         if (res)
00650                 return res;
00651         
00652         return newphid;
00653 }
00654 
00669 int ipc_connect_me_to_blocking(int phoneid, sysarg_t arg1, sysarg_t arg2,
00670     sysarg_t arg3)
00671 {
00672         sysarg_t newphid;
00673         int res = ipc_call_sync_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3,
00674             IPC_FLAG_BLOCKING, NULL, NULL, NULL, NULL, &newphid);
00675         if (res)
00676                 return res;
00677         
00678         return newphid;
00679 }
00680 
00688 int ipc_hangup(int phoneid)
00689 {
00690         return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
00691 }
00692 
00710 int ipc_forward_fast(ipc_callid_t callid, int phoneid, sysarg_t imethod,
00711     sysarg_t arg1, sysarg_t arg2, unsigned int mode)
00712 {
00713         return __SYSCALL6(SYS_IPC_FORWARD_FAST, callid, phoneid, imethod, arg1,
00714             arg2, mode);
00715 }
00716 
00717 int ipc_forward_slow(ipc_callid_t callid, int phoneid, sysarg_t imethod,
00718     sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
00719     unsigned int mode)
00720 {
00721         ipc_call_t data;
00722         
00723         IPC_SET_IMETHOD(data, imethod);
00724         IPC_SET_ARG1(data, arg1);
00725         IPC_SET_ARG2(data, arg2);
00726         IPC_SET_ARG3(data, arg3);
00727         IPC_SET_ARG4(data, arg4);
00728         IPC_SET_ARG5(data, arg5);
00729         
00730         return __SYSCALL4(SYS_IPC_FORWARD_SLOW, callid, phoneid, (sysarg_t) &data,
00731             mode);
00732 }
00733 
00745 int ipc_share_in_start(int phoneid, void *dst, size_t size, sysarg_t arg,
00746     unsigned int *flags)
00747 {
00748         sysarg_t tmp_flags = 0;
00749         int res = ipc_call_sync_3_2(phoneid, IPC_M_SHARE_IN, (sysarg_t) dst,
00750             (sysarg_t) size, arg, NULL, &tmp_flags);
00751         
00752         if (flags)
00753                 *flags = (unsigned int) tmp_flags;
00754         
00755         return res;
00756 }
00757 
00771 int ipc_share_in_finalize(ipc_callid_t callid, void *src, unsigned int flags)
00772 {
00773         return ipc_answer_2(callid, EOK, (sysarg_t) src, (sysarg_t) flags);
00774 }
00775 
00785 int ipc_share_out_start(int phoneid, void *src, unsigned int flags)
00786 {
00787         return ipc_call_sync_3_0(phoneid, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
00788             (sysarg_t) flags);
00789 }
00790 
00803 int ipc_share_out_finalize(ipc_callid_t callid, void *dst)
00804 {
00805         return ipc_answer_1(callid, EOK, (sysarg_t) dst);
00806 }
00807 
00817 int ipc_data_read_start(int phoneid, void *dst, size_t size)
00818 {
00819         return ipc_call_sync_2_0(phoneid, IPC_M_DATA_READ, (sysarg_t) dst,
00820             (sysarg_t) size);
00821 }
00822 
00837 int ipc_data_read_finalize(ipc_callid_t callid, const void *src, size_t size)
00838 {
00839         return ipc_answer_2(callid, EOK, (sysarg_t) src, (sysarg_t) size);
00840 }
00841 
00851 int ipc_data_write_start(int phoneid, const void *src, size_t size)
00852 {
00853         return ipc_call_sync_2_0(phoneid, IPC_M_DATA_WRITE, (sysarg_t) src,
00854             (sysarg_t) size);
00855 }
00856 
00870 int ipc_data_write_finalize(ipc_callid_t callid, void *dst, size_t size)
00871 {
00872         return ipc_answer_2(callid, EOK, (sysarg_t) dst, (sysarg_t) size);
00873 }
00874 
00878 int ipc_connect_kbox(task_id_t id)
00879 {
00880 #ifdef __32_BITS__
00881         sysarg64_t arg = (sysarg64_t) id;
00882         return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) &arg);
00883 #endif
00884         
00885 #ifdef __64_BITS__
00886         return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) id);
00887 #endif
00888 }
00889 

Generated on Thu Jun 2 07:45:46 2011 for HelenOS/USB by  doxygen 1.4.7