summaryrefslogtreecommitdiff
path: root/Source/DirectFB/lib/fusion/call.c
diff options
context:
space:
mode:
Diffstat (limited to 'Source/DirectFB/lib/fusion/call.c')
-rwxr-xr-xSource/DirectFB/lib/fusion/call.c581
1 files changed, 581 insertions, 0 deletions
diff --git a/Source/DirectFB/lib/fusion/call.c b/Source/DirectFB/lib/fusion/call.c
new file mode 100755
index 0000000..71ce5e5
--- /dev/null
+++ b/Source/DirectFB/lib/fusion/call.c
@@ -0,0 +1,581 @@
+/*
+ (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org)
+ (c) Copyright 2000-2004 Convergence (integrated media) GmbH
+
+ All rights reserved.
+
+ Written by Denis Oliver Kropp <dok@directfb.org>,
+ Andreas Hundt <andi@fischlustig.de>,
+ Sven Neumann <neo@directfb.org>,
+ Ville Syrjälä <syrjala@sci.fi> and
+ Claudio Ciccani <klan@users.sf.net>.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <fusion/build.h>
+
+#include <direct/debug.h>
+#include <direct/messages.h>
+
+#include <fusion/types.h>
+#include <fusion/call.h>
+#include <fusion/conf.h>
+
+#include "fusion_internal.h"
+
+
+D_DEBUG_DOMAIN( Fusion_Call, "Fusion/Call", "Fusion Call" );
+
+
+#if FUSION_BUILD_MULTI
+
+#if FUSION_BUILD_KERNEL
+
+DirectResult
+fusion_call_init (FusionCall *call,
+ FusionCallHandler handler,
+ void *ctx,
+ const FusionWorld *world)
+{
+ FusionCallNew call_new;
+
+ D_DEBUG_AT( Fusion_Call, "%s( %p, %p <%s>, %p, %p )\n", __FUNCTION__, call, handler,
+ direct_trace_lookup_symbol_at( handler ), ctx, world );
+
+ D_ASSERT( call != NULL );
+ D_ASSERT( handler != NULL );
+ D_MAGIC_ASSERT( world, FusionWorld );
+ D_MAGIC_ASSERT( world->shared, FusionWorldShared );
+
+ /* Called from others. */
+ call_new.handler = handler;
+ call_new.ctx = ctx;
+
+ while (ioctl( world->fusion_fd, FUSION_CALL_NEW, &call_new )) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ default:
+ break;
+ }
+
+ D_PERROR ("FUSION_CALL_NEW");
+
+ return DR_FAILURE;
+ }
+
+ memset( call, 0, sizeof(FusionCall) );
+
+ /* Store handler, called directly when called by ourself. */
+ call->handler = handler;
+ call->ctx = ctx;
+
+ /* Store call and own fusion id. */
+ call->call_id = call_new.call_id;
+ call->fusion_id = fusion_id( world );
+
+ /* Keep back pointer to shared world data. */
+ call->shared = world->shared;
+
+ D_DEBUG_AT( Fusion_Call, " -> call id %d\n", call->call_id );
+
+ return DR_OK;
+}
+
+DirectResult
+fusion_call_execute (FusionCall *call,
+ FusionCallExecFlags flags,
+ int call_arg,
+ void *call_ptr,
+ int *ret_val)
+{
+ D_DEBUG_AT( Fusion_Call, "%s( %p, 0x%x, %d, %p )\n", __FUNCTION__, call, flags, call_arg, call_ptr );
+
+ D_ASSERT( call != NULL );
+
+ if (!call->handler)
+ return DR_DESTROYED;
+
+ D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler ) );
+
+ if (!(flags & FCEF_NODIRECT) && call->fusion_id == _fusion_id( call->shared )) {
+ int ret;
+ FusionCallHandlerResult result;
+
+ result = call->handler( _fusion_id( call->shared ), call_arg, call_ptr, call->ctx, 0, &ret );
+
+ if (result != FCHR_RETURN)
+ D_WARN( "local call handler returned FCHR_RETAIN, need FCEF_NODIRECT" );
+
+ if (ret_val)
+ *ret_val = ret;
+ }
+ else {
+ FusionCallExecute execute;
+
+ execute.call_id = call->call_id;
+ execute.call_arg = call_arg;
+ execute.call_ptr = call_ptr;
+ execute.flags = flags;
+
+ while (ioctl( _fusion_fd( call->shared ), FUSION_CALL_EXECUTE, &execute )) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ case EINVAL:
+// D_ERROR ("Fusion/Call: invalid call\n");
+ return DR_INVARG;
+ case EIDRM:
+ return DR_DESTROYED;
+ default:
+ break;
+ }
+
+ D_PERROR ("FUSION_CALL_EXECUTE");
+
+ return DR_FAILURE;
+ }
+
+ if (ret_val)
+ *ret_val = execute.ret_val;
+ }
+
+ return DR_OK;
+}
+
+DirectResult
+fusion_call_return( FusionCall *call,
+ unsigned int serial,
+ int val )
+{
+ FusionCallReturn call_ret;
+
+ D_DEBUG_AT( Fusion_Call, "%s( %p, %u, %d )\n", __FUNCTION__, call, serial, val );
+
+ D_ASSERT( call != NULL );
+
+ D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler ) );
+
+ D_ASSUME( serial != 0 );
+ if (!serial)
+ return DR_UNSUPPORTED;
+
+ call_ret.call_id = call->call_id;
+ call_ret.val = val;
+ call_ret.serial = serial;
+
+ while (ioctl (_fusion_fd( call->shared ), FUSION_CALL_RETURN, &call_ret)) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ case EIDRM:
+ D_WARN( "caller withdrawn (signal?)" );
+ return DR_NOCONTEXT;
+ case EINVAL:
+ D_ERROR( "Fusion/Call: invalid call\n" );
+ return DR_DESTROYED;
+ default:
+ break;
+ }
+
+ D_PERROR ("FUSION_CALL_RETURN");
+
+ return DR_FAILURE;
+ }
+
+ return DR_OK;
+}
+
+DirectResult
+fusion_call_destroy (FusionCall *call)
+{
+ D_DEBUG_AT( Fusion_Call, "%s( %p )\n", __FUNCTION__, call );
+
+ D_ASSERT( call != NULL );
+ D_ASSERT( call->handler != NULL );
+
+ D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler ) );
+
+ while (ioctl (_fusion_fd( call->shared ), FUSION_CALL_DESTROY, &call->call_id)) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ case EINVAL:
+ D_ERROR ("Fusion/Call: invalid call\n");
+ return DR_DESTROYED;
+ default:
+ break;
+ }
+
+ D_PERROR ("FUSION_CALL_DESTROY");
+
+ return DR_FAILURE;
+ }
+
+ call->handler = NULL;
+
+ return DR_OK;
+}
+
+void
+_fusion_call_process( FusionWorld *world, int call_id, FusionCallMessage *msg )
+{
+ FusionCallHandler call_handler;
+ FusionCallReturn call_ret;
+ FusionCallHandlerResult result;
+
+ D_DEBUG_AT( Fusion_Call, "%s()\n", __FUNCTION__ );
+
+ D_MAGIC_ASSERT( world, FusionWorld );
+ D_ASSERT( msg != NULL );
+
+ call_handler = msg->handler;
+
+ D_ASSERT( call_handler != NULL );
+
+ D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call_handler ) );
+
+ call_ret.val = 0;
+
+ result = call_handler( msg->caller, msg->call_arg, msg->call_ptr, msg->ctx, msg->serial, &call_ret.val );
+
+ switch (result) {
+ case FCHR_RETURN:
+ if (msg->serial) {
+ call_ret.serial = msg->serial;
+ call_ret.call_id = call_id;
+
+ while (ioctl (world->fusion_fd, FUSION_CALL_RETURN, &call_ret)) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ case EIDRM:
+ D_WARN( "caller withdrawn (signal?)" );
+ return;
+ case EINVAL:
+ D_ERROR( "Fusion/Call: invalid call\n" );
+ return;
+ default:
+ D_PERROR( "FUSION_CALL_RETURN" );
+ return;
+ }
+ }
+ }
+ break;
+
+ case FCHR_RETAIN:
+ break;
+
+ default:
+ D_BUG( "unknown result %d from call handler", result );
+ }
+}
+
+#else /* FUSION_BUILD_KERNEL */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+
+DirectResult
+fusion_call_init (FusionCall *call,
+ FusionCallHandler handler,
+ void *ctx,
+ const FusionWorld *world)
+{
+ D_ASSERT( call != NULL );
+ D_ASSERT( handler != NULL );
+ D_MAGIC_ASSERT( world, FusionWorld );
+ D_MAGIC_ASSERT( world->shared, FusionWorldShared );
+
+ memset( call, 0, sizeof(FusionCall) );
+
+ call->call_id = ++world->shared->call_ids;
+
+ /* Store handler, called directly when called by ourself. */
+ call->handler = handler;
+ call->ctx = ctx;
+
+ /* Store own fusion id. */
+ call->fusion_id = fusion_id( world );
+
+ /* Keep back pointer to shared world data. */
+ call->shared = world->shared;
+
+ return DR_OK;
+}
+
+DirectResult
+fusion_call_execute (FusionCall *call,
+ FusionCallExecFlags flags,
+ int call_arg,
+ void *call_ptr,
+ int *ret_val)
+{
+ DirectResult ret = DR_OK;
+ FusionWorld *world;
+ FusionCallMessage msg;
+ struct sockaddr_un addr;
+
+ D_ASSERT( call != NULL );
+
+ if (!call->handler)
+ return DR_DESTROYED;
+
+ if (!(flags & FCEF_NODIRECT) && call->fusion_id == _fusion_id( call->shared )) {
+ int ret;
+ FusionCallHandlerResult result;
+
+ result = call->handler( _fusion_id( call->shared ), call_arg, call_ptr, call->ctx, 0, &ret );
+
+ if (result != FCHR_RETURN)
+ D_WARN( "local call handler returned FCHR_RETAIN, need FCEF_NODIRECT" );
+
+ if (ret_val)
+ *ret_val = ret;
+
+ return DR_OK;
+ }
+
+ world = _fusion_world( call->shared );
+
+ msg.type = FMT_CALL;
+ msg.caller = world->fusion_id;
+ msg.call_id = call->call_id;
+ msg.call_arg = call_arg;
+ msg.call_ptr = call_ptr;
+ msg.handler = call->handler;
+ msg.ctx = call->ctx;
+ msg.flags = flags;
+
+ if (flags & FCEF_ONEWAY) {
+ /* Invalidate serial. */
+ msg.serial = -1;
+
+ /* Send message. */
+ addr.sun_family = AF_UNIX;
+ snprintf( addr.sun_path, sizeof(addr.sun_path),
+ "/tmp/.fusion-%d/%lx", call->shared->world_index, call->fusion_id );
+
+ ret = _fusion_send_message( world->fusion_fd, &msg, sizeof(msg), &addr );
+ }
+ else {
+ int fd;
+ socklen_t len;
+ int err;
+
+ fd = socket( PF_LOCAL, SOCK_RAW, 0 );
+ if (fd < 0) {
+ D_PERROR( "Fusion/Call: Error creating local socket!\n" ) ;
+ return DR_IO;
+ }
+
+ /* Set close-on-exec flag. */
+ fcntl( fd, F_SETFD, FD_CLOEXEC );
+
+ addr.sun_family = AF_UNIX;
+ len = snprintf( addr.sun_path, sizeof(addr.sun_path),
+ "/tmp/.fusion-%d/call.%x.", fusion_world_index( world ), call->call_id );
+
+ /* Generate call serial (socket address is based on it). */
+ for (msg.serial = 0; msg.serial <= 0xffffff; msg.serial++) {
+ snprintf( addr.sun_path+len, sizeof(addr.sun_path)-len, "%x", msg.serial );
+ err = bind( fd, (struct sockaddr*)&addr, sizeof(addr) );
+ if (err == 0) {
+ chmod( addr.sun_path, 0660 );
+ /* Change group, if requested. */
+ if (fusion_config->shmfile_gid != (gid_t)-1)
+ chown( addr.sun_path, -1, fusion_config->shmfile_gid );
+ break;
+ }
+ }
+
+ if (err < 0) {
+ D_PERROR( "Fusion/Call: Error binding local socket!\n" );
+ close( fd );
+ return DR_IO;
+ }
+
+ /* Send message. */
+ snprintf( addr.sun_path, sizeof(addr.sun_path),
+ "/tmp/.fusion-%d/%lx", call->shared->world_index, call->fusion_id );
+
+ ret = _fusion_send_message( fd, &msg, sizeof(msg), &addr );
+ if (ret == DR_OK) {
+ FusionCallReturn callret;
+ /* Wait for reply. */
+ ret = _fusion_recv_message( fd, &callret, sizeof(callret), NULL );
+ if (ret == DR_OK) {
+ if (ret_val)
+ *ret_val = callret.val;
+ }
+ }
+
+ len = sizeof(addr);
+ if (getsockname( fd, (struct sockaddr*)&addr, &len ) == 0)
+ unlink( addr.sun_path );
+ close( fd );
+ }
+
+ return ret;
+}
+
+DirectResult
+fusion_call_return( FusionCall *call,
+ unsigned int serial,
+ int val )
+{
+ struct sockaddr_un addr;
+ FusionCallReturn callret;
+
+ D_ASSERT( call != NULL );
+
+ addr.sun_family = AF_UNIX;
+ snprintf( addr.sun_path, sizeof(addr.sun_path),
+ "/tmp/.fusion-%d/call.%x.%x", call->shared->world_index, call->call_id, serial );
+
+ callret.type = FMT_CALLRET;
+ callret.val = val;
+
+ return _fusion_send_message( _fusion_fd( call->shared ), &callret, sizeof(callret), &addr );
+}
+
+DirectResult
+fusion_call_destroy (FusionCall *call)
+{
+ D_ASSERT( call != NULL );
+ D_ASSERT( call->handler != NULL );
+
+ call->handler = NULL;
+
+ return DR_OK;
+}
+
+void
+_fusion_call_process( FusionWorld *world, int call_id, FusionCallMessage *msg )
+{
+ FusionCallHandler call_handler;
+ FusionCallHandlerResult result;
+ FusionCallReturn callret;
+
+ D_MAGIC_ASSERT( world, FusionWorld );
+ D_ASSERT( msg != NULL );
+
+ call_handler = msg->handler;
+
+ D_ASSERT( call_handler != NULL );
+
+ callret.type = FMT_CALLRET;
+ callret.val = 0;
+
+ result = call_handler( msg->caller, msg->call_arg, msg->call_ptr, msg->ctx, msg->serial, &callret.val );
+ switch (result) {
+ case FCHR_RETURN:
+ if (!(msg->flags & FCEF_ONEWAY)) {
+ struct sockaddr_un addr;
+
+ addr.sun_family = AF_UNIX;
+ snprintf( addr.sun_path, sizeof(addr.sun_path),
+ "/tmp/.fusion-%d/call.%x.%x", fusion_world_index( world ), call_id, msg->serial );
+
+ if (_fusion_send_message( world->fusion_fd, &callret, sizeof(callret), &addr ))
+ D_ERROR( "Fusion/Call: Couldn't send call return (serial: 0x%08x)!\n", msg->serial );
+ }
+ break;
+
+ case FCHR_RETAIN:
+ break;
+
+ default:
+ D_BUG( "unknown result %d from call handler", result );
+ break;
+ }
+}
+
+#endif /* FUSION_BUILD_KERNEL */
+
+#else /* FUSION_BUILD_MULTI */
+
+DirectResult
+fusion_call_init (FusionCall *call,
+ FusionCallHandler handler,
+ void *ctx,
+ const FusionWorld *world)
+{
+ D_ASSERT( call != NULL );
+ D_ASSERT( call->handler == NULL );
+ D_ASSERT( handler != NULL );
+
+ /* Called locally. */
+ call->handler = handler;
+ call->ctx = ctx;
+
+ return DR_OK;
+}
+
+DirectResult
+fusion_call_execute (FusionCall *call,
+ FusionCallExecFlags flags,
+ int call_arg,
+ void *call_ptr,
+ int *ret_val)
+{
+ FusionCallHandlerResult ret;
+ int val = 0;
+
+ D_ASSERT( call != NULL );
+
+ if (!call->handler)
+ return DR_DESTROYED;
+
+ ret = call->handler( 1, call_arg, call_ptr, call->ctx, 0, &val );
+ if (ret != FCHR_RETURN)
+ D_WARN( "only FCHR_RETURN supported in single app core at the moment" );
+
+ if (ret_val)
+ *ret_val = val;
+
+ return DR_OK;
+}
+
+DirectResult
+fusion_call_return( FusionCall *call,
+ unsigned int serial,
+ int val )
+{
+ return DR_UNIMPLEMENTED;
+}
+
+DirectResult
+fusion_call_destroy (FusionCall *call)
+{
+ D_ASSERT( call != NULL );
+ D_ASSERT( call->handler != NULL );
+
+ call->handler = NULL;
+
+ return DR_OK;
+}
+
+#endif
+